關於連結串列的一些操作總結
連結串列反轉
這是一個簡單的連結串列操作問題,在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
這是關於連結串列的兩種比較簡單的操作,反轉和快慢指標。挺常見的面試題,在這裡做一些記錄分享。