No. 24 - Swap Nodes in Pairs ==== ![Problem](https://img.shields.io/badge/problem-linked%20list-blue) ![Difficulty](https://img.shields.io/badge/difficulty-medium-yellow) --- ## [LeetCode 24 -- Swap Nodes in Pairs](https://leetcode.com/problems/swap-nodes-in-pairs/) ### 題目描述 Given a linked list, swap every two adjacent nodes and return its head. For example: Before swap, ```graphviz digraph { node [shape = circle]; rankdir=LR 2, 4 [color=lightblue,style=filled] 1->2->3->4 } ``` After swap, ```graphviz digraph { node [shape = circle]; rankdir=LR 2, 4 [color=lightblue,style=filled] 2->1->4->3 } ``` Example 1. ``` Input: head = [1,2,3,4] Output: [2,1,4,3] ``` Example 2. ``` Input: head = [1,2,3] Output: [2,1,3] ``` --- ### 解法 本題目的是將 linked list 上每相鄰的兩個 nodes 相互交換。 我們以下面這個 linked list 為例,這個 linked list 從 node 1 到 6 兩兩進行相互交換,也就是說淺藍色的 node 會跟其左邊的白色 node 互換。 ```graphviz digraph linkedList{ node [shape = circle]; rankdir=LR; 2, 4, 6 [color=lightblue,style=filled]; 1 -> 2; 2 -> 3; 3 -> 4; 4 -> 5; 5 -> 6; } ``` 首先,我們先講解相鄰 nodes 互換的作法。 當我們將兩個 nodes 互換時,它們所指向的下個 node 也必須改變。以下面的例子來說,原本 node 2 的下個 node 是 node 3 而 node 1 的下個 node 2,但經過互換後 node 2 的下個 node 改成 node 1 而 node 1 的下個 node 為 node 3。 ```graphviz digraph linkedList{ label="互換前" node [shape = circle]; rankdir=LR; 2[color=lightblue,style=filled]; 1 -> 2; 2 -> 3; } ``` ```graphviz digraph linkedList{ label="互換後" node [shape = circle]; rankdir=LR; 2[color=lightblue,style=filled]; 2 -> 1; 1 -> 3; } ``` 1. 我們首先知道當前的 node 指標是在 node 1 (紅色代表當前指標) ```graphviz digraph linkedList{ node [shape = circle]; rankdir=LR; 1 [color=red,style=filled]; 1 -> 2; 2 -> 3; } ``` 2. 接著我們需要宣告一個新的 node 來記錄當前的 node 指向的下一個 node (用綠色代表)。 ```graphviz digraph linkedList{ node [shape = circle]; rankdir=LR; 2 [color=green,style=filled]; 1 [color=red,style=filled]; 1 -> 2; 2 -> 3; } ``` 3. 最後只要將當前 node (紅色 node) 指向下一個 node 的指標指向下一個 node (綠色 node) 的下一個 node (node 3),再將下一個 node (綠色 node) 指向下一個 node 的指標指回當前 node (紅色 node) 即可完成互換。 ```graphviz digraph linkedList{ node [shape = circle]; rankdir=LR; 2 [color=green,style=filled]; 1 [color=red,style=filled]; 2 -> 1; 1 -> 3; } ``` node 互換的程式如下, head 為給定的當前 node 指標 (紅色 node),我們要另外宣告一個 `node` 儲存當前 node 的下一個 node (綠色 node)。 ```CPP= ListNode *node = head->next; head->next = node->next; node->next = head; return node; ``` 而題目是要我們將整個 linked list 的 node 都兩兩互換,我們可以用前面講解的互換方法搭配遞迴的概念來實現。 我們仍用前面 node 1 到 node 6 的 linked list 範例來講解。 相較於從頭開始互換,用遞迴的概念來思考題目的話,它會像是從最後面開始互換。也就是說如下面的圖,node 3, 4 互換前 node 5, 6 會先互換好,並且 node 4 指向的下一個 node 是 node 5, 6 互換完的結果。 ```graphviz digraph linkedList{ subgraph cluster_0 { node [shape = circle]; rankdir=LR; color=red; 6 [color=lightblue,style=filled]; 6->5; } node [shape = circle]; rankdir=LR; 2,4,6 [color=lightblue,style=filled]; 1 -> 2; 2 -> 3; 3 -> 4; 4 -> 6; } ``` 依此類推,最前面的 node 1, 2 要互換時,後面的 node 3-6 也已經互換好了。我們可以把 node 2 所指向的下一個 node 看成是另外一組要做互換的 linked list 的開頭,而這個子 linked list 也透過一樣的方法進行互換,透過遞迴的方式來把整個 linked list 互換完成。 ```graphviz digraph linkedList{ node [shape = circle]; rankdir=LR; 2 [color=lightblue,style=filled]; 1 -> 2; 2 -> sub_linked_list } ``` 整個 linked list 互換的程式如下,我們可以看到第 16-19 行就是前面講解 nodes 互換的做法,只是在第 17 行 `head` node 要跟下一個 node 互換需要改變指向下一個 node 的指標時,它所指的就像上圖的 `sub_linked_list`,是一個被 `swapPairs()` 互換完成的子 linked list。 另外,由於是兩兩 nodes 進行互換,當 nodes 是奇數的時候會有 node 落單不用互換。所以遞迴停止的條件不只是看當前 node 存不存在,還要看下一個 node 存不存在需不需要互換,這就是第 14-15 行判斷遞迴終止的條件。 ```CPP= /** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode() : val(0), next(nullptr) {} * ListNode(int x) : val(x), next(nullptr) {} * ListNode(int x, ListNode *next) : val(x), next(next) {} * }; */ class Solution { public: ListNode* swapPairs(ListNode* head) { if (!head || !head->next) return head; ListNode *node = head->next; head->next = swapPair(node->next); node->next = head return node; } }; ``` ### 複雜度分析 在分析時間複雜度前,因為我們是用遞迴的方法,我們可以先列出遞迴關係式:$$T(N)=T(N-2)+O(1)$$這個遞迴關係式還滿簡單的,只需將其展開我們可得出其時間複雜度為 $O(N)$。 由於我們需要遞迴呼叫 `swapPairs` 函式,所以需要呼叫堆疊 (call stack) 來儲存每一次遞迴的函式,而此函式只會宣告固定的指標,所以需要 $O(N)$ 的空間複雜度。 :::info 另外也可用迭代的方式達成互換,若是用迭代的方式則空間複雜度為 $O(1)$,因為不需要呼叫堆疊來儲存遞迴的函式。 :::