--- title: "刷題模式: 原地反轉鏈表(In-place Reversal of Linked-List)" description: "Patterns for Coding Questions: Depth-First Search." image: https://i.imgur.com/Wnnr2qC.png author: Hsins tags: LeetCode, Coding Interview robots: breaks: GA: disqus: --- # 原地反轉鏈表(In-place Reversal of Linked-List) <p style="text-align: center"> <img src="https://i.imgur.com/Wnnr2qC.png" height=200/> </p> > 本篇內容主要為 [Grokking the Coding Interview: Patterns for Coding Questions](https://www.educative.io/courses/grokking-the-coding-interview) 的翻譯與整理,行有餘力建議可以購買該專欄課程閱讀並在上面進行練習。 ## 重點整理 - 原地反轉鏈表(In-place Reversal of Linked-List) - 策略:使用現有的節點物件實例,而不使用額外的記憶體 - 題型: - 樹的逐層順序遍歷 - 樹的層級相關操作 ## 題目匯總 - `0025` Reverse Linked List II - `0092` Reverse Nodes in k-Group ## [例題] Reverse a Linked-List ### 問題 給定一個單向鏈結串列(singly linked-list)的頭節點,反轉此鏈結串列,撰寫一函數返回鏈結串列反轉後的頭節點。 **Example** <p style="text-align: center"> <img src="https://i.imgur.com/QBiNhuw.png" /> </p> ### 題解 為了反轉鏈結串列,我們需要每一次反轉一個節點。我們使用變數 `current` 在一開始指向鏈結串列的頭節點,並用變數 `previous` 指向遍歷過的節點,在最一開始他將指向 `null`。 在後續的步驟中,我們透過在向下一個節點前進之前,將當前節點 `current` 指向 `previous` 來反轉當前節點;同時,我們需要更新 `previous` 為剛處理過的節點。上述演算法如圖所示: <p style="text-align: center"> <img src="https://i.imgur.com/qhkwlsH.png"> <img src="https://i.imgur.com/9DKQqBf.png"> <img src="https://i.imgur.com/FgKmXKU.png"> <img src="https://i.imgur.com/lFGGKdK.png"> <img src="https://i.imgur.com/kf5C57h.png"> <img src="https://i.imgur.com/qibrGQs.png"> <img src="https://i.imgur.com/rwQQ3GX.png"> </p> ### 代碼 #### C++ ``` cpp class ReverseLinkedList { public: static ListNode *reverse(ListNode *head) { ListNode *curr = head; ListNode *prev = nullptr; ListNode *next = nullptr; while (curr != nullptr) { next = curr->next; curr->next = prev; prev = curr; curr = next; } return prev; } } ``` #### Java ``` java class ReverseLinkedList { public static ListNode reverse(ListNode head) { ListNode curr = head; ListNode prev = null; ListNode next = null; while (curr != null) { next = curr.next; curr.next = prev; prev = curr; curr = next; } return prev; } } ``` #### JavaScript ``` javascript const reverse = (head) => { let curr = head; let prev = null; let next = null; while (curr !== null) { next = curr.next; curr.next = prev; prev = curr; curr = next; } return prev; } ``` #### Python ``` python def reverse(head): prev, curr, next = None, head, None while curr is not None: next = curr.next curr.next = prev prev = curr curr = next return prev ``` ### 分析 - 時間複雜度:$\mathcal{O}(n)$,其中 $n$ 為鏈結串列中的節點總數 - 空間複雜度:$\mathcal{O}(1)$ ## [例題] Reverse a Sub-List ### 問題 給定一個鏈結串列(singly linked-list)與兩個位置 `p` 和 `q`,反轉鏈結串列中 `p` 至 `q` 之間的部分。 **Example** <p style="text-align: center"> <img src="https://i.imgur.com/WPVKFra.png" /> </p> ### 題解 這一題遵循 **原地反轉鏈表(In-place Reversal of Linked-List)** 策略,我們將使用與 [Reverse a Linked-List](#例題-Reverse-a-Linked-List) 相同的處理方式。步驟如下: 1. 省略前面 `p-1` 個結點,直到抵達位置 `p` 2. 保存第 `p-1` 個結點的位置,用來串起後續反轉部分鏈結串列 3. 使用 [Reverse a Linked-List](#例題-Reverse-a-Linked-List) 的解決方法翻轉 `p` 到 `q` 之間的鏈結串列 4. 將上述翻轉的部分與 `p-1` 和 `q+1` 連結 ### 代碼 #### C++ ``` cpp ``` #### Java ``` java class ReverseSubList { public static ListNode reverse(ListNode head, int p, int q) { if (p == q) return head; // skipping 'p-1' nodes, curr will point to 'p' node ListNode curr = head; ListNode prev = null; for (int i = 0; curr != null && i < p - 1; ++i) { prev = curr; curr = curr.next; } // focus three parts: [0, p-1] [p, q] [q+1, -1] ListNode lastNodeOfFirstPart = prev; ListNode lastNodeOfSubList = curr; ListNode next = null; for (int i = 0; curr != null && i < q - p + 1; ++i) { next = curr.next; curr.next = prev; prev = curr; curr = next; } // connect parts if (lastNodeOfFirstPart != null) lastNodeOfFirstPart.next = prev; else head = prev; lastNodeOfSubList.next = curr; return head; } } ``` #### JavaScript ``` javascript ``` #### Python ``` python ``` ### 分析 - 時間複雜度:$\mathcal{O}(n)$,其中 $n$ 為鏈結串列中的節點總數 - 空間複雜度:$\mathcal{O}(1)$ ## [例題] Reverse every K-element Sub-List ### 問題 ### 題解 ### 代碼 #### C++ ``` cpp ``` #### Java ``` java ``` #### JavaScript ``` javascript ``` #### Python ``` python ``` ### 分析 - 時間複雜度:$\mathcal{O}(n)$,其中 $n$ 為鏈結串列中的節點總數 - 空間複雜度:$\mathcal{O}(1)$ ## 參考資料 - [Coding Patterns: In-place Reversal of a Linked List | emre.me](https://emre.me/coding-patterns/in-place-reversal-of-a-linked-list/) - [Inplace Linked List Reversal Pattern | Astik Anand](https://astikanand.github.io/techblogs/coding-problem-patterns/inplace-linked-list-reversal-pattern)