【算】鏈表反轉

問題

最近研究算法,遇到的一道頗有意思的問題——怎麼把一個鏈表反轉?
很容易想到一個方法:遍歷鏈表,數組做棧存儲路徑,元素逐個出棧獲得的就是反轉後的鏈表!查找資料發現,有更好的方式實現。java

仔細研究後,終於明白了其中的奧妙。小僧掌握了兩種方法,如下分別進行說明。node

clipboard.png

首先給出鏈表結構:git

public class LinkedNode {
    Integer id ;
    LinkedNode next;
    
    public LinkedNode(Integer id) {
        this.id = id;
    }
}

下一步構造出上圖的鏈表結構:github

LinkedNode node1 = new LinkedNode(1);
LinkedNode node2 = new LinkedNode(2);
LinkedNode node3 = new LinkedNode(3);
LinkedNode node4 = new LinkedNode(4);
node1.next = node2;
node2.next = node3;
node3.next = node4;

雙指針遍歷法

先給出代碼實現:算法

/**
 * 鏈表翻轉,循環 + 雙指針(pre、next)實現
 * @param cur
 * @return
 */
public LinkedNode reverse(LinkedNode cur){
    LinkedNode pre = null;

    while (cur!=null){
        LinkedNode next = cur.next; // 1.
        cur.next = pre; // 2.
        pre = cur;  // 3.
        cur = next; // 4.
    }

    return pre;
}

循環體以前,鏈表示意圖:
clipboard.png數組

以後進入while循環,註釋標註的四個步驟會產生以下變化(圖中編號與註釋編號一一對應):
clipboard.pngthis

第一次循環後,鏈表變成這樣:
clipboard.pngspa

以後的遍歷,鏈表的變化示意:
clipboard.png指針

可見,while循環執行完,pre指向的節點,已是最新的頭節點了!code

遞歸法

遞歸的實現方式,彷佛更容易理解。

/**
 * 鏈表反轉,遞歸實現
 * @param node
 * @return
 */
public LinkedNode reverse2(LinkedNode node){
    if(node.next==null){
        return node;
    }

    LinkedNode newHead = reverse2(node.next);
    node.next.next = node;  //node.next.next 換成 newHead.next 不行,由於node在遞歸中在追溯上一個節點,仔細體會下
    node.next = null;
    return newHead;
}

首先會經過遞歸調用找到尾節點,以後作了兩件事:

  1. 尾節點反指 (node.next.next = node;)
  2. 當前節點指向null節點 (node.next = null;)

clipboard.png

rentun newHead後,回溯到節點2(此時node就是節點2),再次重複以前的兩件事——節點反指和當前節點指向null節點。
clipboard.png

再次回溯,獲得最終的結果。
clipboard.png

老規矩,完整代碼見git:暗夜君王的demo練習——鏈表反轉

Done !

相關文章
相關標籤/搜索