[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. 針對大規模鏈表,應選擇迭代法以降低空間複雜度。

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

歡迎來到我的部落格!這裡記錄了軟體工程師的日常生活點滴,並分享程式設計與演算法的實用教學。無論你是初學者還是有經驗的開發者,都能在這裡找到深入淺出的技術解析與實戰技巧。此外,我也會分享工作中的心路歷程與學習心得,讓你不僅學到技術,更能瞭解軟體開發的實際應用與挑戰。希望透過這個平台,能與你共同成長,激發對技術的熱情!
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
給定一個鏈表,每次交換相鄰的兩個節點,返回交換後的鏈表。要求在不改變節點值的情況下進行節點交換(即直接操作鏈表結構)。
給定 k 個遞增排序的鏈表,將它們合併成一個遞增排序的鏈表,並返回合併後的鏈表。
給定一個整數 n,請生成所有可能由 n 對括號組成的 有效括號組合。
給定兩個遞增排序的鏈表 list1 和 list2,將它們合併為一個新的 遞增排序 的鏈表,並返回這個新鏈表。新的鏈表應該由這兩個鏈表中的節點組成,而不應創建新的節點。最後回傳合併後的鍊錶的頭節點。
給定一個僅包含字元 '(', ')', '{', '}', '[' 和 ']' 的字串 s,確定輸入字串是否有效。
給定一個Linked list的head,從這個Linked list的末尾刪除第 n 個節點並返回其head。
給定一個鏈表,每次交換相鄰的兩個節點,返回交換後的鏈表。要求在不改變節點值的情況下進行節點交換(即直接操作鏈表結構)。
給定 k 個遞增排序的鏈表,將它們合併成一個遞增排序的鏈表,並返回合併後的鏈表。
給定一個整數 n,請生成所有可能由 n 對括號組成的 有效括號組合。
給定兩個遞增排序的鏈表 list1 和 list2,將它們合併為一個新的 遞增排序 的鏈表,並返回這個新鏈表。新的鏈表應該由這兩個鏈表中的節點組成,而不應創建新的節點。最後回傳合併後的鍊錶的頭節點。
給定一個僅包含字元 '(', ')', '{', '}', '[' 和 ']' 的字串 s,確定輸入字串是否有效。
給定一個Linked list的head,從這個Linked list的末尾刪除第 n 個節點並返回其head。
你可能也想看
Google News 追蹤
Thumbnail
最近國泰世華CUBE App推出的「美股定期定額」功能,讓使用者可以方便地進行跨境理財(但讀者仍需根據自身需求審慎考量),除了享有美股定期定額的新功能,也同時享有台股定期定額的功能,可以一站滿足我們理財的需求! 透過國泰世華CUBE App線上開台股證券戶+複委託戶,流程最快僅需要5分鐘。
Thumbnail
嘿,大家新年快樂~ 新年大家都在做什麼呢? 跨年夜的我趕工製作某個外包設計案,在工作告一段落時趕上倒數。 然後和兩個小孩過了一個忙亂的元旦。在深夜時刻,看到朋友傳來的解籤網站,興致勃勃熬夜體驗了一下,覺得非常好玩,或許有人玩過了,但還是想寫上來分享紀錄一下~
Thumbnail
題目敘述 Merge Nodes in Between Zeros 給定一個鏈結串列,合併非零區間的節點(以加總的方式合併),輸出合併後的鏈結串列。
Thumbnail
題目敘述 題目給定一個鏈結串列中的節點Node,要求我們從鏈結串列中刪除該節點。 題目保證該節點不是tail node。 題目要求我們in-place原位操作。 原本的英文題目敘述 測試範例 Example 1: Input: head = [4,5,1,9], node = 5
Thumbnail
題目敘述 輸入給定一個鏈結串列的head node。 要求我們進行化簡,只要某個節點的右手邊存在比較大的節點,就刪除掉。 例如 5->2->13->3 5的右手邊有13,所以5刪除掉。 2的右手邊有13,所以2刪除掉。 13的右手邊沒有更大的節點,所以13留著。 3的右手邊沒有更大
Thumbnail
這篇文章,會帶著大家複習以前學過的遞回框架, 並且鏈結串列的概念與應用為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 遞回框架 尋找共通模式(common pattern),對應到演算法的General case 確立初始條件(initial conditio
Thumbnail
題目會給定一個陣列nums和一個目標值goal。計算子陣列總和=goal的數目有多少。演算法包含前綴和和字典的技巧,時間複雜度為O(n),空間複雜度為O(n)。
Thumbnail
題目敘述 題目會給定一個鏈結串列的起始點,要求我們把其中區間總和為0的部分刪除掉。 例如 1→ 2 → -2 → 3 → 4 裡面有一段是2 → -2 區間總和為零,所以簡化刪除後變成 1→ 3 → 4 題目的原文敘述 測試範例 Example 1: Input: head
Thumbnail
題目敘述 題目會給定一個鏈結串列 Linked List的頭部結點,要求我們根據索引的奇偶數重新排列。奇數索引的在前,偶數索引的在後。數的時候,從Head節點的索引=1開始數。 例如: 1 -> 2 -> 3 -> 4 -> 5 重新排列為 1 -> 3 -> 5 -> 2 -> 4
Thumbnail
題目敘述 題目會給定我們一條鏈結串列Linked list的起始節點,要求我們刪除Linked List正中央的節點。 註: 正中央的節點,題目定義為索引為floor( 串列長度 / 2 ) 的節點,索引從零(Head Node)出發開始數。 例如 1 -> 2 -> 3 -> 4 鏈結
Thumbnail
題目敘述 題目給定我們一個輸入陣列nums 要求我們以正、負交叉排列的方式重組陣列,並且必須保持原本的相對順序。 並且以陣列的形式輸出返回答案。 例[5, 1, -2, -3] 重排後為 [5, -2, 1, -3] 題目的原文敘述 測試範例 Example 1: Input:
Thumbnail
題目敘述 題目會給我們一個輸入陣列nums,和一個指定的k值。 請問,在輸入陣列nums中,有幾個子陣列的元素總合恰好為k ? 例如: nums = [1,2,3], k = 3 則有兩個子陣列的元素總合為3,分別是[1,2] 和 [3] 如果是第一次聽到或接觸前綴和prefix的同學
Thumbnail
最近國泰世華CUBE App推出的「美股定期定額」功能,讓使用者可以方便地進行跨境理財(但讀者仍需根據自身需求審慎考量),除了享有美股定期定額的新功能,也同時享有台股定期定額的功能,可以一站滿足我們理財的需求! 透過國泰世華CUBE App線上開台股證券戶+複委託戶,流程最快僅需要5分鐘。
Thumbnail
嘿,大家新年快樂~ 新年大家都在做什麼呢? 跨年夜的我趕工製作某個外包設計案,在工作告一段落時趕上倒數。 然後和兩個小孩過了一個忙亂的元旦。在深夜時刻,看到朋友傳來的解籤網站,興致勃勃熬夜體驗了一下,覺得非常好玩,或許有人玩過了,但還是想寫上來分享紀錄一下~
Thumbnail
題目敘述 Merge Nodes in Between Zeros 給定一個鏈結串列,合併非零區間的節點(以加總的方式合併),輸出合併後的鏈結串列。
Thumbnail
題目敘述 題目給定一個鏈結串列中的節點Node,要求我們從鏈結串列中刪除該節點。 題目保證該節點不是tail node。 題目要求我們in-place原位操作。 原本的英文題目敘述 測試範例 Example 1: Input: head = [4,5,1,9], node = 5
Thumbnail
題目敘述 輸入給定一個鏈結串列的head node。 要求我們進行化簡,只要某個節點的右手邊存在比較大的節點,就刪除掉。 例如 5->2->13->3 5的右手邊有13,所以5刪除掉。 2的右手邊有13,所以2刪除掉。 13的右手邊沒有更大的節點,所以13留著。 3的右手邊沒有更大
Thumbnail
這篇文章,會帶著大家複習以前學過的遞回框架, 並且鏈結串列的概念與應用為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 遞回框架 尋找共通模式(common pattern),對應到演算法的General case 確立初始條件(initial conditio
Thumbnail
題目會給定一個陣列nums和一個目標值goal。計算子陣列總和=goal的數目有多少。演算法包含前綴和和字典的技巧,時間複雜度為O(n),空間複雜度為O(n)。
Thumbnail
題目敘述 題目會給定一個鏈結串列的起始點,要求我們把其中區間總和為0的部分刪除掉。 例如 1→ 2 → -2 → 3 → 4 裡面有一段是2 → -2 區間總和為零,所以簡化刪除後變成 1→ 3 → 4 題目的原文敘述 測試範例 Example 1: Input: head
Thumbnail
題目敘述 題目會給定一個鏈結串列 Linked List的頭部結點,要求我們根據索引的奇偶數重新排列。奇數索引的在前,偶數索引的在後。數的時候,從Head節點的索引=1開始數。 例如: 1 -> 2 -> 3 -> 4 -> 5 重新排列為 1 -> 3 -> 5 -> 2 -> 4
Thumbnail
題目敘述 題目會給定我們一條鏈結串列Linked list的起始節點,要求我們刪除Linked List正中央的節點。 註: 正中央的節點,題目定義為索引為floor( 串列長度 / 2 ) 的節點,索引從零(Head Node)出發開始數。 例如 1 -> 2 -> 3 -> 4 鏈結
Thumbnail
題目敘述 題目給定我們一個輸入陣列nums 要求我們以正、負交叉排列的方式重組陣列,並且必須保持原本的相對順序。 並且以陣列的形式輸出返回答案。 例[5, 1, -2, -3] 重排後為 [5, -2, 1, -3] 題目的原文敘述 測試範例 Example 1: Input:
Thumbnail
題目敘述 題目會給我們一個輸入陣列nums,和一個指定的k值。 請問,在輸入陣列nums中,有幾個子陣列的元素總合恰好為k ? 例如: nums = [1,2,3], k = 3 則有兩個子陣列的元素總合為3,分別是[1,2] 和 [3] 如果是第一次聽到或接觸前綴和prefix的同學