[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
追極光的北極熊|軟體工程師的小天地
9會員
150內容數
歡迎來到我的部落格!這裡記錄了軟體工程師的日常生活點滴,並分享程式設計與演算法的實用教學。無論你是初學者還是有經驗的開發者,都能在這裡找到深入淺出的技術解析與實戰技巧。此外,我也會分享工作中的心路歷程與學習心得,讓你不僅學到技術,更能瞭解軟體開發的實際應用與挑戰。希望透過這個平台,能與你共同成長,激發對技術的熱情!
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
每年4月、5月都是最多稅要繳的月份,當然大部份的人都是有機會繳到「綜合所得稅」,只是相當相當多人還不知道,原來繳給政府的稅!可以透過一些有活動的銀行信用卡或電子支付來繳,從繳費中賺一點點小確幸!就是賺個1%~2%大家也是很開心的,因為你們把沒回饋變成有回饋,就是用卡的最高境界 所得稅線上申報
Thumbnail
每年4月、5月都是最多稅要繳的月份,當然大部份的人都是有機會繳到「綜合所得稅」,只是相當相當多人還不知道,原來繳給政府的稅!可以透過一些有活動的銀行信用卡或電子支付來繳,從繳費中賺一點點小確幸!就是賺個1%~2%大家也是很開心的,因為你們把沒回饋變成有回饋,就是用卡的最高境界 所得稅線上申報
Thumbnail
全球科技產業的焦點,AKA 全村的希望 NVIDIA,於五月底正式發布了他們在今年 2025 第一季的財報 (輝達內部財務年度為 2026 Q1,實際日曆期間為今年二到四月),交出了打敗了市場預期的成績單。然而,在銷售持續高速成長的同時,川普政府加大對於中國的晶片管制......
Thumbnail
全球科技產業的焦點,AKA 全村的希望 NVIDIA,於五月底正式發布了他們在今年 2025 第一季的財報 (輝達內部財務年度為 2026 Q1,實際日曆期間為今年二到四月),交出了打敗了市場預期的成績單。然而,在銷售持續高速成長的同時,川普政府加大對於中國的晶片管制......
Thumbnail
重點摘要: 6 月繼續維持基準利率不變,強調維持高利率主因為關稅 點陣圖表現略為鷹派,收斂 2026、2027 年降息預期 SEP 連續 2 季下修 GDP、上修通膨預測值 --- 1.繼續維持利率不變,強調需要維持高利率是因為關稅: 聯準會 (Fed) 召開 6 月利率會議
Thumbnail
重點摘要: 6 月繼續維持基準利率不變,強調維持高利率主因為關稅 點陣圖表現略為鷹派,收斂 2026、2027 年降息預期 SEP 連續 2 季下修 GDP、上修通膨預測值 --- 1.繼續維持利率不變,強調需要維持高利率是因為關稅: 聯準會 (Fed) 召開 6 月利率會議
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]
Thumbnail
題目的輸入會給我們一個串列,要求我們從頭到尾反轉整個串列。 例子: 如果輸入是1 -> 2 -> 3,那麼輸出就是 3 -> 2 -> 1
Thumbnail
題目的輸入會給我們一個串列,要求我們從頭到尾反轉整個串列。 例子: 如果輸入是1 -> 2 -> 3,那麼輸出就是 3 -> 2 -> 1
Thumbnail
Input: head = [1,2,3,4,5], n = 2 Output: [1,2,3,5]
Thumbnail
Input: head = [1,2,3,4,5], n = 2 Output: [1,2,3,5]
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News