單鏈表反轉是數據結構與算法中的經典問題,它不僅考察對鏈表結構的理解,也考驗編程思維和技巧。本文將帶你從基礎實現到高級應用,全面掌握單鏈表反轉。
1. 理解單鏈表
在深入反轉算法之前,我們先回顧單鏈表的基本結構:
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
單鏈表的特點是每個節點包含數據和指向下一個節點的指針,只能單向遍歷。
2. 基礎反轉方法
2.1 迭代法(最常用)
核心思想:逐個改變節點指向,從前往後反轉。
def reverse_list_iterative(head):
prev = None
current = head
while current:
# 保存下一個節點
next_temp = current.next
# 反轉指針
current.next = prev
# 移動指針
prev = current
current = next_temp
return prev # 新的頭節點
執行過程可視化:
原鏈表: 1 → 2 → 3 → 4 → 5 → None
步驟1: None ← 1 2 → 3 → 4 → 5 → None
步驟2: None ← 1 ← 2 3 → 4 → 5 → None
步驟3: None ← 1 ← 2 ← 3 4 → 5 → None
...
結果: None ← 1 ← 2 ← 3 ← 4 ← 5
2.2 遞歸法
核心思想:通過遞歸到達鏈表末端,然後從後往前反轉。
def reverse_list_recursive(head):
# 遞歸終止條件
if not head or not head.next:
return head
# 遞歸到最後一個節點
new_head = reverse_list_recursive(head.next)
# 反轉指針
head.next.next = head
head.next = None
return new_head
遞歸過程分析:
reverse(1)
reverse(2)
reverse(3)
reverse(4)
reverse(5) → 返回5
5.next = 4, 4.next = None
5.next = 3, 3.next = None
5.next = 2, 2.next = None
5.next = 1, 1.next = None
3. 進階反轉技巧
3.1 反轉部分鏈表
反轉鏈表中從位置 m 到 n 的部分:
def reverse_between(head, m, n):
if not head or m == n:
return head
dummy = ListNode(0)
dummy.next = head
prev = dummy
# 移動到第m-1個節點
for _ in range(m - 1):
prev = prev.next
# 開始反轉
current = prev.next
for _ in range(n - m):
temp = current.next
current.next = temp.next
temp.next = prev.next
prev.next = temp
return dummy.next
3.2 K個一組反轉鏈表
每 k 個節點一組進行反轉,不足 k 的保持原樣:
def reverse_k_group(head, k):
def reverse_sublist(start, end):
prev, curr = None, start
while curr != end:
next_temp = curr.next
curr.next = prev
prev = curr
curr = next_temp
return prev
# 檢查是否有k個節點
node = head
for _ in range(k):
if not node:
return head
node = node.next
# 反轉前k個節點
new_head = reverse_sublist(head, node)
# 遞歸處理剩餘部分
head.next = reverse_k_group(node, k)
return new_head
3.3 交替反轉
第一個k個節點反轉,下一個k個節點保持,如此交替:
def reverse_alternate_k_nodes(head, k):
if not head or k <= 1:
return head
current = head
prev = None
reverse = True
while current:
if reverse:
# 反轉k個節點
section_start = current
section_prev = None
count = 0
# 檢查是否有足夠的節點
temp = current
for _ in range(k):
if not temp:
break
temp = temp.next
# 反轉當前段
while current and count < k:
next_temp = current.next
current.next = section_prev
section_prev = current
current = next_temp
count += 1
# 連接已反轉部分
if prev:
prev.next = section_prev
else:
head = section_prev
section_start.next = current
prev = section_start
reverse = False
else:
# 跳過k個節點
count = 0
while current and count < k:
prev = current
current = current.next
count += 1
reverse = True
return head
4. 特殊場景反轉
4.1 反轉鏈表的前N個節點
def reverse_first_n(head, n):
successor = None
def reverse_n(node, count):
nonlocal successor
if count == 1:
successor = node.next
return node
last = reverse_n(node.next, count - 1)
node.next.next = node
node.next = successor
return last
return reverse_n(head, n)
4.2 從末尾開始反轉
def reverse_from_end(head, k):
# 先找到總長度
length = 0
current = head
while current:
length += 1
current = current.next
# 轉換為從頭開始的位置
m = length - k + 1
return reverse_between(head, m, length)
5. 性能分析與比較
| 方法 | 時間複雜度 | 空間複雜度 | 適用場景 |
|---|---|---|---|
| 迭代法 | O(n) | O(1) | 通用,最常用 |
| 遞歸法 | O(n) | O(n) | 代碼簡潔,棧深度受限 |
| 部分反轉 | O(n) | O(1) | 局部操作 |
| K組反轉 | O(n) | O(n/k) | 批量處理 |
6. 實戰技巧與注意事項
6.1 邊界情況處理
- 空鏈表
- 單節點鏈表
- 反轉位置超出範圍
- k值大於鏈表長度
6.2 調試技巧
def print_list(head):
"""打印鏈表用於調試"""
result = []
current = head
while current:
result.append(str(current.val))
current = current.next
print(" → ".join(result) + " → None")
6.3 常見錯誤
- 忘記處理頭節點的更新
- 指針丟失(在改變next前未保存)
- 循環鏈表的形成
- 邊界條件處理不完整
7. 綜合練習
題目:給定鏈表 1→2→3→4→5→6→7,要求實現:
- 完全反轉
- 反轉2-5位置
- 每3個一組反轉
- 交替每2個節點反轉
# 測試用例
def create_sample_list():
nodes = [ListNode(i) for i in range(1, 8)]
for i in range(len(nodes)-1):
nodes[i].next = nodes[i+1]
return nodes[0]
# 測試各種反轉方法
head = create_sample_list()
print("原鏈表:")
print_list(head)
print("\n完全反轉:")
print_list(reverse_list_iterative(create_sample_list()))
print("\n反轉位置2-5:")
print_list(reverse_between(create_sample_list(), 2, 5))
print("\n每3個一組反轉:")
print_list(reverse_k_group(create_sample_list(), 3))
總結
單鏈表反轉是算法學習中的重要里程碑。掌握從基礎到進階的各種反轉技巧,不僅能夠解決鏈表相關問題,更能培養嚴謹的編程思維和指針操作能力。建議通過大量練習來熟練掌握這些技巧,並理解每種方法適用的場景。
記住:多畫圖理解指針變化,多寫代碼加深印象,這是掌握鏈表問題的關鍵!