[LeetCode解題攻略] 25. Reverse Nodes in k-Group

更新 發佈閱讀 13 分鐘

題目描述

給定一個鏈表,將鏈表的節點每 k 個一組進行反轉,並返回修改後的鏈表。如果節點總數不是 k 的倍數,則保留最後剩餘節點的順序。要求在不改變節點值的情況下進行反轉(即直接操作鏈表結構)。

條件

  • 鏈表的長度範圍是 0 <= n <= 5000
  • 節點的值範圍是 0 <= Node.val <= 1000
  • 1 <= k <= n

範例 1:

Input: head = [1,2,3,4,5], k = 2
Output: [2,1,4,3,5]
Explanation:2 個節點一組進行反轉,結果為 [2,1,4,3,5]

範例 2:

Input: head = [1,2,3,4,5], k = 3
Output: [3,2,1,4,5]
Explanation:3 個節點一組進行反轉,結果為 [3,2,1,4,5]

範例 3:

Input: head = [1,2,3,4,5], k = 1
Output: [1,2,3,4,5]
Explanation: k = 1 時,鏈表保持不變。

範例 4:

Input: head = [1], k = 1
Output: [1]

解題思路

該問題的核心在於分段反轉鏈表。可以通過以下步驟來完成:

  1. 遍歷鏈表,找出每 k 節點的範圍。
  2. 將這 k 個節點進行反轉,並將反轉後的部分重新鏈接到整體鏈表。
  3. 處理剩餘不足 k 個的部分,保持原樣。

解法一:模擬分組反轉

思路

  1. 使用一個指針遍歷整個鏈表,找到每 k 個節點作為一組。
  2. 每次反轉這一組內的節點,並將其鏈接到已處理的部分。
  3. 如果剩餘節點不足 k,則不進行反轉。

程式碼

# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reverseKGroup(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
# 計算鏈表長度
def getLength(node):
length = 0
while node:
length += 1
node = node.next
return length

# 反轉一段鏈表
def reverseList(start, end):
prev = None
current = start
while current != end:
temp = current.next
current.next = prev
prev = current
current = temp
return prev

# 虛擬節點
dummy = ListNode(0)
dummy.next = head
prev_group = dummy

# 獲取鏈表長度
length = getLength(head)

# 遍歷鏈表,每 k 個為一組進行反轉
while length >= k:
group_start = prev_group.next
group_end = group_start
for _ in range(k - 1):
group_end = group_end.next

next_group = group_end.next

# 反轉當前組
group_end.next = None
prev_group.next = reverseList(group_start, None)
group_start.next = next_group

# 更新指針
prev_group = group_start
length -= k

return dummy.next

時間與空間複雜度

  • 時間複雜度
    • 每次反轉操作需要遍歷 k 個節點,總共有 n/k 次反轉,因此時間複雜度為 O(n),其中 n 是鏈表的節點數。
  • 空間複雜度
    • 使用了常數額外空間,空間複雜度為 O(1)。

解法二:遞迴法

思路

  1. 對於前 k 個節點進行反轉。
  2. 剩下的部分使用遞迴處理,並將反轉後的部分接回已處理的部分。
  3. 基本情況:如果剩餘節點不足 k,則返回原鏈表。

程式碼

# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reverseKGroup(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
# 檢查是否有足夠的節點可以反轉
current = head
count = 0
while current and count < k:
current = current.next
count += 1

if count < k:
return head

# 反轉前 k 個節點
prev = None
current = head
for _ in range(k):
temp = current.next
current.next = prev
prev = current
current = temp

# 遞迴處理剩餘部分
head.next = self.reverseKGroup(current, k)

return prev

時間與空間複雜度

  • 時間複雜度
    • 與解法一相同,每次遞迴處理 k 個節點,因此時間複雜度為 O(n)。
  • 空間複雜度
    • 使用遞迴函數調用棧,最深遞迴深度為 n/k,因此空間複雜度為 O(n/k)。

解法三:迭代法

此方法與解法一相似,進行少量優化以提高效率和代碼可讀性。

思路

迭代法的核心在於使用指針遍歷鏈表,逐步反轉每一組 k 個節點,並在反轉完成後重新將每組節點正確地鏈接起來。過程中,保持指針的更新和臨時保存反轉區間的節點信息。

步驟如下:

  1. 使用一個虛擬節點(dummy node)方便操作頭節點的連接。
  2. 設置指針追蹤每組需要反轉的起點和終點。
  3. 反轉 k 個節點,並記錄反轉後的頭尾節點。
  4. 更新指針以處理下一組節點,直到鏈表中剩餘節點不足 k。

程式碼

# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reverseKGroup(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:

# 輔助函數:反轉單向鏈表
def reverseList(head):
prev = None
current = head
while current:
temp = current.next
current.next = prev
prev = current
current = temp
return prev, head # 返回新的頭節點和尾節點

# 建立虛擬節點
dummy = ListNode(0)
dummy.next = head
prev_group = dummy # 追蹤每組反轉後的尾節點

while True:
# 找到需要反轉的一組節點
group_start = prev_group.next
group_end = prev_group
for _ in range(k):
group_end = group_end.next
if not group_end:
return dummy.next # 如果不足 k 個節點,直接返回結果

# 保存下一組的起點
next_group = group_end.next
group_end.next = None # 暫時斷開鏈表

# 反轉當前組
reversed_head, reversed_tail = reverseList(group_start)

# 將反轉後的節點接回原鏈表
prev_group.next = reversed_head
reversed_tail.next = next_group

# 更新指針,為下一組做準備
prev_group = reversed_tail

時間與空間複雜度

  • 時間複雜度
    • 每次反轉一組 k 個節點需要 O(k),鏈表總共有 n/k 組,因此時間複雜度為 O(n)。
  • 空間複雜度
    • 僅使用了指針進行操作,額外空間為常數,因此空間複雜度為 O(1)。

總結

  1. 模擬分組反轉 是最直觀的解法,適合初學者。
  2. 遞迴法 更具可讀性,但遞迴深度可能成為性能瓶頸。
  3. 針對大規模鏈表,應選擇迭代法以降低空間複雜度。

選擇適合的解法取決於具體場景,對於需要高效處理的應用場景,迭代法更為優秀。

留言
avatar-img
留言分享你的想法!
avatar-img
追極光的北極熊|軟體工程師的小天地
12會員
163內容數
歡迎來到我的部落格!這裡記錄了軟體工程師的日常生活點滴,並分享程式設計與演算法的實用教學。無論你是初學者還是有經驗的開發者,都能在這裡找到深入淺出的技術解析與實戰技巧。此外,我也會分享工作中的心路歷程與學習心得,讓你不僅學到技術,更能瞭解軟體開發的實際應用與挑戰。希望透過這個平台,能與你共同成長,激發對技術的熱情!
2025/04/04
給定一個非負整數陣列 nums,其中 nums[i] 代表你在索引 i 處最多可以向右跳幾步。 請判斷是否能夠從索引 0 跳到最後一個索引。
2025/04/04
給定一個非負整數陣列 nums,其中 nums[i] 代表你在索引 i 處最多可以向右跳幾步。 請判斷是否能夠從索引 0 跳到最後一個索引。
2025/03/28
給定一個 m x n 的矩陣 matrix,請按照 螺旋順序(spiral order) 返回矩陣中的所有元素。
2025/03/28
給定一個 m x n 的矩陣 matrix,請按照 螺旋順序(spiral order) 返回矩陣中的所有元素。
2025/03/20
這道題是 LeetCode 的經典題之一,要求我們找出 一個子陣列,使其和最大,並返回這個最大和的數值。
2025/03/20
這道題是 LeetCode 的經典題之一,要求我們找出 一個子陣列,使其和最大,並返回這個最大和的數值。
看更多
你可能也想看
Thumbnail
雙11於許多人而言,不只是單純的折扣狂歡,更是行事曆裡預定的,對美好生活的憧憬。 錢錢沒有不見,它變成了快樂,跟讓臥房、辦公桌、每天早晨的咖啡香升級的樣子! 這次格編突擊辦公室,也邀請 vocus「野格團」創作者分享掀開蝦皮購物車的簾幕,「加入購物車」的瞬間,藏著哪些靈感,或是對美好生活的想像?
Thumbnail
雙11於許多人而言,不只是單純的折扣狂歡,更是行事曆裡預定的,對美好生活的憧憬。 錢錢沒有不見,它變成了快樂,跟讓臥房、辦公桌、每天早晨的咖啡香升級的樣子! 這次格編突擊辦公室,也邀請 vocus「野格團」創作者分享掀開蝦皮購物車的簾幕,「加入購物車」的瞬間,藏著哪些靈感,或是對美好生活的想像?
Thumbnail
Insert Greatest Common Divisors in Linked List 題目給定一個鏈結串列, 請在兩兩節點之間加入一個新節點,新節點的值為兩者之間的最大公因數。 最後返回新串列的head node作為答案。
Thumbnail
Insert Greatest Common Divisors in Linked List 題目給定一個鏈結串列, 請在兩兩節點之間加入一個新節點,新節點的值為兩者之間的最大公因數。 最後返回新串列的head node作為答案。
Thumbnail
題目敘述 題目給定一個鏈結串列中的節點Node,要求我們從鏈結串列中刪除該節點。 題目保證該節點不是tail node。 題目要求我們in-place原位操作。 原本的英文題目敘述 測試範例 Example 1: Input: head = [4,5,1,9], node = 5
Thumbnail
題目敘述 題目給定一個鏈結串列中的節點Node,要求我們從鏈結串列中刪除該節點。 題目保證該節點不是tail node。 題目要求我們in-place原位操作。 原本的英文題目敘述 測試範例 Example 1: Input: head = [4,5,1,9], node = 5
Thumbnail
題目敘述 題目會給定一個鏈結串列 Linked List的頭部結點,要求我們根據索引的奇偶數重新排列。奇數索引的在前,偶數索引的在後。數的時候,從Head節點的索引=1開始數。 例如: 1 -> 2 -> 3 -> 4 -> 5 重新排列為 1 -> 3 -> 5 -> 2 -> 4
Thumbnail
題目敘述 題目會給定一個鏈結串列 Linked List的頭部結點,要求我們根據索引的奇偶數重新排列。奇數索引的在前,偶數索引的在後。數的時候,從Head節點的索引=1開始數。 例如: 1 -> 2 -> 3 -> 4 -> 5 重新排列為 1 -> 3 -> 5 -> 2 -> 4
Thumbnail
題目會給定我們一個串列,和一個n值,要求我們刪除尾巴數來的第n個節點。 例如 1->2->3->4->5 和 給定n值=2,要求我們刪除尾巴數來的第2個節點。 尾巴數來的第2個節點是4,刪除之後,更新連結,輸出答案如下 1->2->3->5
Thumbnail
題目會給定我們一個串列,和一個n值,要求我們刪除尾巴數來的第n個節點。 例如 1->2->3->4->5 和 給定n值=2,要求我們刪除尾巴數來的第2個節點。 尾巴數來的第2個節點是4,刪除之後,更新連結,輸出答案如下 1->2->3->5
Thumbnail
題目會給定我們一個陣列和參數k,要求我們將陣列右旋k次,然後輸出內容。 例如[1,2,3,4,5] 右旋 2次,輸出就是[4,5,1,2,3]
Thumbnail
題目會給定我們一個陣列和參數k,要求我們將陣列右旋k次,然後輸出內容。 例如[1,2,3,4,5] 右旋 2次,輸出就是[4,5,1,2,3]
Thumbnail
題目:在此 kata 中,您將創建一個包含列表並返回具有相反順序的列表的函數。範例(輸入->輸出) * [1, 2, 3, 4] -> [4, 3, 2, 1] * [9, 2, 0, 7] -> [7, 0, 2, 9]
Thumbnail
題目:在此 kata 中,您將創建一個包含列表並返回具有相反順序的列表的函數。範例(輸入->輸出) * [1, 2, 3, 4] -> [4, 3, 2, 1] * [9, 2, 0, 7] -> [7, 0, 2, 9]
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News