1. 程式人生 > 實用技巧 >LeetCode Notes_#24_兩兩交換連結串列中的節點

LeetCode Notes_#24_兩兩交換連結串列中的節點

LeetCode Notes_#24_兩兩交換連結串列中的節點

LeetCode

Contents

題目


解答

方法1:迭代+修改指標

一開始自己寫,發現有兩個難點不好解決:

  1. 最後無法得到修改後連結串列的頭節點
  2. 第一組的兩個節點沒法連線到第二組的兩個節點,舉例來說就是1沒法連線到4

問題在於:

  1. 頭節點需要通過啞節點dummyHead.next來獲取,沒有啞節點的話,是沒法處理這個問題的
  2. 每次迭代開始的位置應該是“上一組的兩個節點的尾巴”,舉例來說,第二輪迭代時,cur
    指標應該是1的位置,而不是3的位置。

具體來說,我們需要維護3個指標,第一個指標指向的是“上一組的兩個節點的尾巴”,後兩個指標則指向下一組節點的兩個節點。這樣就比較方便進行各種指標的修改。

class Solution {
    public ListNode swapPairs(ListNode head) {
        ListNode dummyHead = new ListNode(0);
        dummyHead.next = head;
        ListNode cur = dummyHead;
        while(cur.next != null
&& cur.next.next != null){ ListNode node1 = cur.next; ListNode node2 = cur.next.next; node1.next = node2.next; cur.next = node2; node2.next = node1; cur = cur.next.next; } return dummyHead.next; } }

複雜度分析

時間複雜度:O(n)
空間複雜度:O(1)

方法2:迭代+棧

我們還可以利用棧的性質,將原始連結串列節點入棧,然後將出棧的節點加入新的連結串列,以達到反轉的目的。這樣的方法直接避免了複雜的指標操作,程式碼看起來非常清晰,其實執行效率並不比方法1要低。

class Solution {
    public ListNode swapPairs(ListNode head) {
        //注意:這種方法並不是在原地進行修改,而是建立一條新連結串列,新連結串列的啞頭節點是newListDummy
        ListNode newListDummy = new ListNode(0);
        Deque<ListNode> stack = new LinkedList<>();
        ListNode newListPtr = newListDummy;
        ListNode oldListPtr = head;
        while(oldListPtr != null && oldListPtr.next != null){
            //將原始連結串列上的節點入棧
            stack.addLast(oldListPtr);
            stack.addLast(oldListPtr.next);
            oldListPtr = oldListPtr.next.next;
            //將剛才加入的兩個節點出棧,連線到新連結串列之後
            newListPtr.next = stack.removeLast();
            newListPtr.next.next = stack.removeLast();
            newListPtr = newListPtr.next.next;
        }
        //因為迴圈結束,證明oldListPtr和oldListPtr.next不會同時非null
        //只有兩個可能,第一種是:oldListPtr非null,而ololdListPtr.next是null(對應於奇數個節點的情況)
        if(oldListPtr != null){
            newListPtr.next = oldListPtr;
        }
        //第二種是:oldListPtr和ololdListPtr.next都是null(對應於偶數個節點的情況)
        else{
            //如果沒有這一句,會導致連結串列中出現環,無法通過
            newListPtr.next = null;
        }
        return newListDummy.next;
    }
}

複雜度分析

時間複雜度:O(n)
空間複雜度:O(n),建立了一條新的連結串列

方法3:遞迴

看到一個很好的圖解,轉載自三道題套路解決遞迴問題 | lyl's blog


這個寫法看起來有點難懂...感覺我自己是根本不會想到這種方法的。

class Solution {
    public ListNode swapPairs(ListNode head) {
        //遞迴終止條件:連結串列只剩一個節點,或者沒有節點了,就沒得交換了,返回已經處理好的連結串列
        if(head == null || head.next == null){
            return head;
        }
        //僅僅是暫存一下head.next,因為接下來head.next要改變了
        ListNode next = head.next;
        //遞迴呼叫,傳入的引數是下下個節點,也就是說,從下一組開始翻轉
        head.next = swapPairs(next.next);
        //這一句和上一句不能交換位置,因為交換過後next就先改變了
        next.next = head;
        return next;
    }
}

複雜度分析

時間複雜度:O(n)
空間複雜度:O(n),遞迴棧空間