就地鍊錶反轉 常見程式設計模式之就地反轉鍊錶

2021-10-14 06:22:01 字數 4210 閱讀 5135

在很多問題中,我們需要對乙個鍊錶中的節點連線進行反轉,且通常需要原地進行,即不能使用額外的儲存空間。這時我們可以使用就地反轉鍊錶模式,該模式本質上是一種迭代解法,流程如下圖所示。首先設定乙個變數current指向鍊錶頭部,以及另乙個變數previous指向當前處理節點的前乙個節點。下一步我們需要將當前節點current進行反轉,將其指向前乙個節點previous,然後將previous指向當前節點,再將current移動到下乙個節點,開始迭代直至遍歷完成。

反轉乙個單鏈表。

「示例」

輸入: 1->2->3->4->5->null

輸出: 5->4->3->2->1->null

class solution:

def reverselist(self, head: listnode) -> listnode:

curr, pre = head, none

while curr:

temp_next = curr.next

curr.next = pre

pre = curr

curr = temp_next

return pre

當然這道題也可以採用遞迴實現,但是使用了隱式的棧空間,且不是非常好理解:

class solution:

def reverselist(self, head: listnode) -> listnode:

if not head or not head.next:

return head

p = self.reverselist(head.next) # 一直遞迴到最後乙個元素之前,返回p為head.next(即最後乙個節點)

head.next.next = head # 在當前遞迴中將head.next的下乙個節點反轉為head

head.next = none # 清除當前節點的下乙個節點,防止迴圈(實際上只對最後乙個節點有用,前面的會自動修改)

return p

遞迴的原理可以通過下圖理解(來自 leetcode-王尼瑪):

反轉從位置 m 到 n 的鍊錶。請使用一趟掃瞄完成反轉。

1 ≤ m ≤ n ≤ 鍊錶長度。

「示例:」

輸入: 1->2->3->4->5->null, m = 2, n = 4

輸出: 1->4->3->2->5->null

這道題可以採用就地反轉鍊錶模式,由於只翻轉部分位置,所以需要設定兩個額外的節點進行標記,翻轉完成後將翻轉部分和未翻轉的部分進行拼接。具體的**實現如下:

class solution:

def reversebetween(self, head: listnode, m: int, n: int) -> listnode:

if not head: return none

cur, prev = head, none

while m > 1: # 將cur移到開始反轉的位置,prev移到其前乙個節點

prev = cur

cur = cur.next

m, n = m - 1, n - 1 # 注意對n也進行處理,相當於n=n-m+1

tail, con = cur, prev # 設定兩個額外節點,用於之後拼接鍊錶

while n: # 執行就地反轉鍊錶模式

temp_next = cur.next

cur.next = prev

prev = cur

cur = temp_next

n -= 1

# 拼接頭部

if con:

con.next = prev

else:

head = prev

# 拼接尾部(實際上最開始的prev被丟棄了)

tail.next = cur

return head

本題也可以採用遞迴方法進行求解,我們先定義乙個反轉前 n 個節點的子函式,然後遞迴至起始位置開始反轉即可:

class solution:

def reversebetween(self, head: listnode, m: int, n: int) -> listnode:

def reversen(head, n):

if n == 1:

return head

p = reversen(head.next, n - 1)

successor = head.next.next # 始終指向反轉節點的後乙個節點

head.next.next = head # 反轉鍊錶

head.next = successor # 將最後乙個節點指向後面未反轉的部分

return p

if m == 1: return reversen(head, n)

head.next = self.reversebetween(head.next, m - 1, n - 1)

return head

25. k 個一組反轉鍊錶(hard)

給你乙個鍊錶,每 k 個節點一組進行翻轉,請你返回翻轉後的鍊錶。k 是乙個正整數,它的值小於或等於鍊錶的長度。

如果節點總數不是 k 的整數倍,那麼請將最後剩餘的節點保持原有順序。

「示例」

給你這個鍊錶:1->2->3->4->5

這道題同樣可以使用就地反轉鍊錶模式,我們需要構建乙個反轉子鍊錶的函式,然後遍歷目標鍊錶,達到 k 個則進行反轉,並將其拼接回主煉表中。這裡的關鍵技巧是通過啞結點來保證每次相同的遍歷次數,以及方便最後的返回。具體的**實現如下:

class solution:

def reversekgroup(self, head: listnode, k: int) -> listnode:

def reverse(head, tail):

# 翻轉乙個子鍊錶,並且返回新的頭與尾

prev = tail.next # 指向尾節點的下乙個節點

curr = head

while prev != tail: # 不能用tail.next判斷,其指向已經改變

temp_next = curr.next

curr.next = prev

prev = curr

curr = temp_next

return tail, head

dummy = listnode(0) # 設定乙個啞結點,方便進行k次遍歷(作為pre)以及最終的返回

dummy.next = head

pre = dummy # pre為子鍊錶的前乙個節點,用於將子鍊錶接回原鍊錶

while head:

tail = pre

# 檢視剩餘的長度是否大於等於k

for i in range(k): # 從起始節點的前乙個結點開始,剛好k次到尾部

tail = tail.next

if not tail:

return dummy.next # 不滿足直接返回

temp_next = tail.next # 記錄子鍊錶的下乙個節點

head, tail = reverse(head, tail) # 反轉子鍊錶

# 把子鍊錶接回原鍊錶,並設定新的pre與head

pre.next = head

tail.next = temp_next

pre = tail

head = tail.next

return dummy.next

就地鍊錶反轉 鍊錶常見問題總結 一

鍊錶 linked list 是一種常見的基礎資料結構,是一種線性表,但是並不會按線性的順序儲存資料,而是在每乙個節點裡存到下乙個節點的指標 pointer 由於不必須按順序儲存,鍊錶在插入的時候可以達到 o 1 o 1 的複雜度,比另一種線性表 順序表快得多,但是查詢乙個節點或者訪問特定編號的節點...

就地鍊錶反轉 單鏈表反轉總結篇

單鏈表的反轉是常見的面試題目。本文總結了2種方法。1 定義 單鏈表node的資料結構定義如下 class listnode 15 return dummy.next 16 2.4 總結 1個頭結點,2個指標,4行 注意初始狀態和結束狀態,體會中間的 過程。3 方法2 新建鍊錶,頭節點插入法 3.1 ...

鍊錶就地逆置

就地逆置,就是在不借助任何中間變數的情況下,逆置一單鏈表。演算法思路 逆置後的點鍊錶初始為空,表中的節點不是新生成的,而是從原鍊錶當中一次 刪除 再逐個頭插到逆置表中。設逆置鍊錶的初始態為空表,刪除 已知鍊錶中 的第乙個節點,然後將它 插入 到逆置鍊錶的 表頭 即使得他成為逆置鍊錶中 新 的第乙個節...