今天羣裏大佬給我拋出了一個問題,單鏈表反轉。
非常仁慈的給了我10分鐘的時間,結果我連鏈表的數據結構都沒寫完,果然我還是太菜了。
雖說我也是看過數據結構的,但是一,時間有點久了;二,當時看的時候就沒怎麼寫代碼。
所以欠的債要還,硬着頭皮也要寫。
最開始我就冒出了一個想法,及其幼稚,思路就是從鏈表中取值,然後把所有值在外部排序完,再放回鏈表中,但是我一看這根本就是離題了啊!可是暫時沒有什麼別的想法,想着寫寫試試吧,結果發現這麼寫好像也挺麻煩的,就放棄了。
然後也想過遞歸方式實現,但是我還是太年輕了,對於遞歸理解的不是很到位,當時認爲使用遞歸的話,不好找前一個節點,這樣就沒辦法設置當前節點的下一個節點。
總之,我還是百度了,看了下思路。
兩種方法:遞歸和遍歷。
下面的方法參考了文章:點這裏
遞歸
首先是遞歸
public static SingleLinkedListNode reversal(SingleLinkedListNode node){
if (node == null || node.next == null){
return node;
}else {
SingleLinkedListNode headNode = reversal(node.next);
node.next.next = node;
node.next = null;
return headNode;
}
}
思路就是從最後一個節點開始,向前反轉。
這裏有一個比較關鍵的點,也是我之前以爲遞歸行不通的癥結就是:遞歸會入棧,而棧中保存了節點所有信息,並且不是設置當前節點的下一節點爲前一節點,而是設置當前節點的下一節點的next爲當前節點。
遍歷
接着是遍歷法
public static SingleLinkedListNode reversal2(SingleLinkedListNode node){
SingleLinkedListNode previousNode = null;
SingleLinkedListNode currentNode = node;
SingleLinkedListNode headNode = null;
while (currentNode != null){
SingleLinkedListNode nextNode = currentNode.next;
if (nextNode == null){
headNode = currentNode;
}
currentNode.next = previousNode;
previousNode = currentNode;
currentNode = nextNode;
}
return headNode;
}
這裏的思路是從前往後反轉,每經過一個節點,就將當前節點的next進行反轉,指向之前的節點。
遍歷另一版
我這裏自己思考實現了另一版,但是沒有上面那個優雅,且考慮的不夠全面
public static SingleLinkedListNode reversal3(SingleLinkedListNode node){
SingleLinkedListNode previousNode = null;
SingleLinkedListNode currentNode = node;
SingleLinkedListNode nextNode = null;
while (currentNode.next != null){
nextNode = currentNode.next;
currentNode.next = previousNode;
previousNode = currentNode;
currentNode = nextNode;
}
currentNode.next = previousNode;
return currentNode;
}
雖然運行起來的時候沒什麼問題,但是我在考慮的時候確實忽略了入參鏈表爲空的情況的。
完整代碼
完整代碼如下
/**
* @Auther: yubt
* @Description: 單鏈表反轉
* @Date: Created in 11:04 2018/9/27
* @Modified By:
*/
public class Reversal_linkedList {
private static class SingleLinkedListNode {
private int data;
private SingleLinkedListNode next;
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
public SingleLinkedListNode getNext() {
return next;
}
public void setNext(SingleLinkedListNode next) {
this.next = next;
}
@Override
public String toString() {
return "SingleLinkedListNode{" +
"data=" + data +
", next=" + next +
'}';
}
}
// 遞歸法
// 運行正常,debug會棧溢出,因爲toString()方法
public static SingleLinkedListNode reversal(SingleLinkedListNode node){
if (node == null || node.next == null){
return node;
}else {
SingleLinkedListNode headNode = reversal(node.next);
node.next.next = node;
node.next = null;
return headNode;
}
}
// 遍歷法
public static SingleLinkedListNode reversal2(SingleLinkedListNode node){
SingleLinkedListNode previousNode = null;
SingleLinkedListNode currentNode = node;
SingleLinkedListNode headNode = null;
while (currentNode != null){
SingleLinkedListNode nextNode = currentNode.next;
if (nextNode == null){
headNode = currentNode;
}
currentNode.next = previousNode;
previousNode = currentNode;
currentNode = nextNode;
}
return headNode;
}
public static SingleLinkedListNode reversal3(SingleLinkedListNode node){
SingleLinkedListNode previousNode = null;
SingleLinkedListNode currentNode = node;
SingleLinkedListNode nextNode = null;
while (currentNode.next != null){
nextNode = currentNode.next;
currentNode.next = previousNode;
previousNode = currentNode;
currentNode = nextNode;
}
currentNode.next = previousNode;
return currentNode;
}
public static void main(String[] args) {
SingleLinkedListNode node1 = new SingleLinkedListNode();
node1.setData(1);
SingleLinkedListNode node2 = new SingleLinkedListNode();
node2.setData(2);
SingleLinkedListNode node3 = new SingleLinkedListNode();
node3.setData(3);
SingleLinkedListNode node4 = new SingleLinkedListNode();
node4.setData(4);
SingleLinkedListNode node5 = new SingleLinkedListNode();
node5.setData(5);
node1.setNext(node2);
node2.setNext(node3);
node3.setNext(node4);
node4.setNext(node5);
// System.out.println(node1);
// SingleLinkedListNode reversalNode = reversal(node1);
// System.out.println(reversalNode);
// SingleLinkedListNode reversalNode2 = reversal2(node1);
// System.out.println(reversalNode2);
SingleLinkedListNode reversalNode3 = reversal3(node1);
System.out.println(reversalNode3);
}
}
所以這些基礎的東西還是要時不時就回顧一下,不會就趕緊補,有遺忘就複習,總之要夯實基礎,雖然工作中不太可能用的上,但是隻會業務邏輯,天天增刪改查也太low了吧。。。
附加題
結果大佬又提了一個問題,存心搞我。
說存在 0 <= m < n <= l
,l
爲鏈表長度,然後要反轉m
和n
之間的部分。
吭呲癟肚一下午,寫了一坨勉強能用的吧
// m,n 從0起
public static SingleLinkedListNode specialReversal(SingleLinkedListNode node, int m, int n){
SingleLinkedListNode headNode = node;
SingleLinkedListNode beforeM = node;
SingleLinkedListNode afterM = null;
SingleLinkedListNode mBetweenN = null;
SingleLinkedListNode afterN = null;
if (m == 0){
beforeM = null;
afterM = node;
}else {
// 截斷m處的節點
for (int i = 0; node != null; i++) {
// 操作next節點偏移量+1
if (i + 1 == m) {
afterM = node.next;
node.next = null;
}
node = node.next;
}
}
// 截斷n處節點,並獲得m和n之間的鏈表
mBetweenN = afterM;
for (int j = 0; j < n - m; j++){
afterM = afterM.next;
}
afterN = afterM.next;
afterM.next = null;
// 對m和n間的鏈表進行反轉
SingleLinkedListNode reversalMN = reversal2(mBetweenN);
// 將n之後的鏈表連接回來
SingleLinkedListNode tmp = reversalMN;
while (reversalMN.next != null){
reversalMN = reversalMN.next;
}
reversalMN.next = afterN;
if (m != 0) {
// 將m之前的鏈表連接回來
while (beforeM.next != null) {
beforeM = beforeM.next;
}
beforeM.next = tmp;
}else {
// 由於m爲0,無鏈表,所以直接替換頭部節點
headNode = tmp;
}
// 返回頭部節點
return headNode;
}
我的思路是把mn之間的鏈表截斷出來,然後反轉之後再接回去。
大佬的思路呢,是直接對mn之間的節點進行交換,比我這個簡單
把他的代碼貼出來把
val dummy = new ListNode(0)
dummy.next = head
var pre = dummy
for (_ <- 0 until m - 1)
pre = pre.next
val start = pre.next
var tail = start.next
for (_ <- 0 until n - m) {
start.next = tail.next
tail.next = pre.next
pre.next = tail
tail = start.next
}
dummy.next
先這樣吧。