一、題目是啥?一句話說清
給你一個鏈表,每k個節點一組進行反轉,如果最后剩余的節點不足k個,則保持原狀。需要實際交換節點,而不僅僅是改變值。
示例:
- 輸入:head = [1,2,3,4,5], k = 2
- 輸出:[2,1,4,3,5](因為每2個一組反轉,最后剩余5不足2個,保持原狀)
二、解題核心
使用虛擬頭節點簡化操作,然后遍歷鏈表,每次檢查是否有k個節點,如果有則反轉這k個節點,并正確連接反轉后的組與前后部分。 這就像處理一列火車車廂,每k節車廂為一組進行調頭,調頭后還要重新連接好前后車廂。
三、關鍵在哪里?(3個核心點)
想理解并解決這道題,必須抓住以下三個關鍵點:
1. 虛擬頭節點(Dummy Node)的使用
- 是什么:在原始鏈表前添加一個不存儲實際數據的節點。
- 為什么重要:反轉后頭節點可能改變(例如原來頭節點是1,反轉后可能變成2),使用虛擬頭節點可以避免單獨處理頭節點變化的特殊情況。
2. 分組檢查與反轉
- 是什么:每次反轉前,先檢查是否還有k個節點可供反轉。如果不足k個,則保持剩余節點原狀。
- 為什么重要:這確保了算法只反轉完整的k個節點組,并正確處理剩余節點。
3. 連接反轉后的組
- 是什么:反轉一組節點后,需要將前一組節點的尾部與反轉后的新頭部連接,并將反轉后的新尾部與下一組節點的頭部連接。
- 為什么重要:如果連接不正確,鏈表會斷開或形成環,導致錯誤。
四、看圖理解流程(通俗理解版本)
讓我們用鏈表 1 → 2 → 3 → 4 → 5 和 k=2 的例子來可視化過程:
-
初始化:
- 創建虛擬頭節點
dummy
,其 next 指向頭節點 1。 - 設置
pointer
指針指向dummy
,用于記錄當前組的前一個節點。 - 初始狀態:dummy → 1 → 2 → 3 → 4 → 5
- 創建虛擬頭節點
-
第一組反轉(反轉1和2):
- 檢查是否有2個節點:從
pointer
開始,有2個節點(1和2)。 - 反轉1和2:
- 初始化:prev = null, curr = 1, next = null
- 第一步:next = 2, curr.next = null, prev = 1, curr = 2
- 第二步:next = 3, curr.next = 1, prev = 2, curr = 3
- 反轉后:2 → 1
- 連接:
pointer.next
(dummy)指向2(新頭),1(新尾)指向3(下一組的頭)。 - 更新
pointer
指向1(新尾)。 - 當前鏈表:dummy → 2 → 1 → 3 → 4 → 5
- 檢查是否有2個節點:從
-
第二組反轉(反轉3和4):
- 檢查是否有2個節點:從
pointer
(1)開始,有2個節點(3和4)。 - 反轉3和4:
- 初始化:prev = null, curr = 3, next = null
- 第一步:next = 4, curr.next = null, prev = 3, curr = 4
- 第二步:next = 5, curr.next = 3, prev = 4, curr = 5
- 反轉后:4 → 3
- 連接:
pointer.next
(1)指向4(新頭),3(新尾)指向5(下一組的頭)。 - 更新
pointer
指向3(新尾)。 - 當前鏈表:dummy → 2 → 1 → 4 → 3 → 5
- 檢查是否有2個節點:從
-
第三組檢查:
- 檢查是否有2個節點:從
pointer
(3)開始,只有1個節點(5),不足2個,因此保持原狀。 - 結束循環。
- 檢查是否有2個節點:從
-
返回結果:返回
dummy.next
,即新頭節點2。
五、C++ 代碼實現(附詳細注釋)
#include <iostream>
using namespace std;// 鏈表節點定義
struct ListNode {<