【LeetCode題解】25_k個一組翻轉連結串列(Reverse-Nodes-in-k-Group)
目錄
更多 LeetCode 題解筆記可以訪問我的 ofollow,noindex" target="_blank">github 。
描述
給出一個連結串列,每 k 個節點一組進行翻轉,並返回翻轉後的連結串列。
k 是一個正整數,它的值小於或等於連結串列的長度。如果節點總數不是 k 的整數倍,那麼將最後剩餘節點保持原有順序。
示例 :
給定這個連結串列: 1->2->3->4->5
當 k = 2 時,應當返回: 2->1->4->3->5
當 k = 3 時,應當返回: 3->2->1->4->5
說明 :
- 你的演算法只能使用常數的額外空間。
- 你不能只是單純的改變節點內部的值 ,而是需要實際的進行節點交換。
解法一:迭代
思路
要求解這道題,先要構造一個輔助函式,這個函式的作用就是翻轉連結串列(不包括首尾節點)。假設輸入連結串列如下圖所示,其中連結串列表頭為 begin
,連結串列尾部為 end
。
經過函式之後,連結串列的連線變為如下的形式,且將 begin
作為函式的輸出。
具體的實現思路和 LeetCode 第206題 翻轉連結串列 是一致的,只是函式的輸入和輸出有些不同,這裡不在贅言,直接給出函式的實現。
// Java 版本 private ListNode reverse(ListNode begin, ListNode end) { ListNode prev = begin, curr = begin.next; ListNode first = curr; while (curr != end) { ListNode next = curr.next; curr.next = prev; prev = curr; curr = next; } begin.next = prev; first.next = curr; return first; }
# Python 版本 def reverse(begin, end): prev, curr, first = begin, begin.next, begin.next while curr != end: curr.next, prev, curr = prev, curr, curr.next begin.next, first.next = prev, curr return first
有個上面的輔助函式之後,我們就可以利用它求解這道題。假設輸入連結串列為 1 -> 2 -> 3 -> 4 -> 5
且 k=3
,首先構造一個虛擬頭節點 dummy
,用於統一後面的一系列操作。初始時,設變數 i=0
,當 i+1
不能被3整除時,將 head
指標向連結串列的下一個節點移動;當 i+1
能被3整除時,呼叫上面的輔助函式,將 begin
節點和 head.next
節點之間的節點進行翻轉。具體的操作可以看下面的圖片演示。
之後,將 head
指標指向 begin
指標的下一個節點(這裡為 4
),即 head = begin.next
。如此迴圈往復,直到 head
節點為 null
,則結束所有操作。
Java 實現
/** * Definition for singly-linked list. * public class ListNode { *int val; *ListNode next; *ListNode(int x) { val = x; } * } */ class Solution { public ListNode reverseKGroup(ListNode head, int k) { // boundary judgement boolean hasNoOrOneNode = (head == null || head.next == null); if (hasNoOrOneNode || k == 1) { return head; } ListNode dummy = new ListNode(-1); dummy.next = head; ListNode begin = dummy; int i = 0; while (head != null) { ++i; if (i % k == 0) { begin = reverse(begin, head.next); head = begin.next; } else { head = head.next; } } return dummy.next; } private ListNode reverse(ListNode begin, ListNode end) { ListNode prev = begin, curr = begin.next; ListNode first = curr; while (curr != end) { ListNode next = curr.next; curr.next = prev; prev = curr; curr = next; } begin.next = prev; first.next = curr; return first; } } // Runtime: 3 ms // Your runtime beats 100.00 % of java submissions.
Python 實現
# Definition for singly-linked list. # class ListNode: #def __init__(self, x): #self.val = x #self.next = None class Solution: def reverseKGroup(self, head, k): """ :type head: ListNode :type k: int :rtype: ListNode """ has_no_or_one_node = (not head or not head.next) if has_no_or_one_node or k == 1: return head dummy = ListNode(-1) dummy.next = head begin = dummy i = 0 while head: i += 1 if i % k == 0: begin = self._reverse(begin, head.next) head = begin.next else: head = head.next return dummy.next def _reverse(self, begin, end): prev, curr, first = begin, begin.next, begin.next while curr != end: curr.next, prev, curr = prev, curr, curr.next begin.next, first.next = prev, curr return first
複雜度分析
- 時間複雜度: \(O(n)\)
- 空間複雜度: \(O(1)\)
解法二:遞迴(不滿足空間複雜度)
思路
遞迴的思路和迭代的思路是一樣的,也是對 k 個為一組的節點進行翻轉,區別在於遞迴是按照從後往前的順序分別對每組節點進行翻轉,而迭代則是從前往後。
Java 實現
/** * Definition for singly-linked list. * public class ListNode { *int val; *ListNode next; *ListNode(int x) { val = x; } * } */ class Solution { public ListNode reverseKGroup(ListNode head, int k) { ListNode curr = head; int i = 0; while (curr != null && i < k) { ++i; curr = curr.next; } if (i == k) { curr = reverseKGroup(curr, k); while (i > 0) { ListNode tmp = head.next; head.next = curr; curr = head; head = tmp; --i; } head = curr; } return head; } }
Python 實現
# Definition for singly-linked list. # class ListNode: #def __init__(self, x): #self.val = x #self.next = None class Solution: def reverseKGroup(self, head, k): """ :type head: ListNode :type k: int :rtype: ListNode """ curr = head i = 0 while curr and i < k: curr = curr.next i += 1 if i == k: curr = self.reverseKGroup(curr, k) while i > 0: head.next, head, curr = curr, head.next, head i -= 1 head = curr return head
複雜度分析
- 時間複雜度: \(O(n)\)
- 空間複雜度: \(O(n)\)