【LeetCode題解】25_k個一組翻轉連結串列(Reverse-Nodes-in-k-Group)
目錄
更多 LeetCode 題解筆記可以訪問我的 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
作為函式的輸出。
具體的實現思路和
// 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)\)