【算】鏈表反轉

問題

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

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

clipboard.png

首先給出鏈表結構:

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

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

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.png

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

之後的遍歷,鏈表的變化示意:
clipboard.png

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

遞歸法

遞歸的實現方式,似乎更容易理解。

/**
 * 鏈表反轉,遞歸實現
 * @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 !

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章