鏈表反轉
這是一個簡單的鏈表操作問題,在leetcode上面有52.7%的通過率,難度是簡單。但是還是想在這裏基於python做一下總結,順便總結一下鏈表的各種操作。
首先先看一下leetcode上面的題目:
反轉一個單鏈表。
示例:
輸入: 1->2->3->4->5->NULL
輸出: 5->4->3->2->1->NULL
進階:
你可以迭代或遞歸地反轉鏈表。你能否用兩種方法解決這道題?
看完了題目,很直白,就是轉過來。我們嘗試對這道題進行解決。這道題用python至少會有三種解決方案。
首先是鏈表的數據結構定義:
# Definition for singly-linked list.
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
- 將鏈表遍歷進list中,然後利用切片反轉list,再將list填充到鏈表中即可。這是最簡單的一種思考邏輯,但是也比較消耗性能。時間和空間複雜度都爲O(n)。
def loop(head):
temp = []
while head is not None:
temp.append(head)
head = head.next
temp = temp[::-1]
for i, n in enumerate(temp):
if i + 1 < len(temp):
n.next = temp[i + 1]
else:
n.next = None
return temp[0] if temp else None
- 另一種迭代算法,是一種純粹交換的迭代,筆者這裏截取了leetcode速度最快的一種。
def reverseList(head):
"""
:type head: ListNode
:rtype: ListNode
"""
if not head:
return None
it = head; jt = head.next
it.next = None
while jt:
tmp = it
it = jt
jt = jt.next
it.next = tmp
return it
這一波交換操作,我們可以畫個示意圖就知道他的交換是一種怎麼樣的交換。
從圖中可以看出,循環的作用就是將反向指針進行保存。同時令將指針轉向的功能。
- 最後一種方案是採用遞歸的方式進行鏈表反轉。這種方式也需要一定的理解。我們先展示一下代碼。
def reverseList(head):
"""
:type head: ListNode
:rtype: ListNode
"""
if head is None or head.next is None:
return head
end = reverseList(head.next)
head.next.next = head
head.next = None
return end
這種解法其實理解起來只有兩部分內容,傳遞反向指針和進行指針反向拼接。我們先來理解一下指針反向拼接這個操作。
1 -> 2 -> 3 -> 4 -> None
依次:
3(head) -> 4(head.next) -> 3(head.next.next)
3 -> None
如此循環即可將鏈表反轉過來。但是還有個關鍵就是將最後一個指針傳遞出來。我們可以看到之前的代碼中,end傳出來後是一直沒有做任何操作的。不停的return出最後一個指針。所以就將最後一個指針傳遞了出來。
以上就是鏈表反轉的3中方法。除此之外還想寫一些鏈表的簡單操作。
快慢指針
何爲快慢指針,即對鏈表進行兩個不同步頻的指針標記遍歷。最經典的是慢指針走一步,快指針走兩步。
快慢指針有很多的應用,比如說:
def hasCycle(head):
"""
:type head: ListNode
:rtype: bool
"""
if head is None or head.next is None:
return False
fast, slow = head.next.next, head.next
while fast is not slow:
if fast is None or fast.next is None:
return False
fast = fast.next.next
slow = slow.next
return True
兩個指針並排走,如果有環的話快指針最終會指向慢指針的位置。沒有環的話,快指針會指向None後退出。
當然這道題的解法不止這一樣,還可以利用set進行判斷。
- 輸出鏈表中的倒數第K個節點
這道題利用快慢指針的思路是這樣的。定義兩個指針,第一個指針向前走k-1步;第2個指針保持不動;到第k步時第2個指針也開始移動。由於兩個指針始終保持着k-1的距離,所以當快指針到達末尾時,慢指針剛好指向倒數第k個。
def count_back(head, k):
if head is None:
return head
fast, slow = head, head
for i in range(k - 1):
fast = fast.next
if fast is None:
return None
while fast is not None:
fast = fast.next
slow = slow.next
return slow
這是關於鏈表的兩種比較簡單的操作,反轉和快慢指針。挺常見的面試題,在這裏做一些記錄分享。