1. 程式人生 > >關於連結串列的一些操作總結

關於連結串列的一些操作總結

連結串列反轉

這是一個簡單的連結串列操作問題,在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
  1. 將連結串列遍歷進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
  1. 另一種迭代演算法,是一種純粹交換的迭代,筆者這裡截取了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

這一波交換操作,我們可以畫個示意圖就知道他的交換是一種怎麼樣的交換。

在這裡插入圖片描述

從圖中可以看出,迴圈的作用就是將反向指標進行儲存。同時令將指標轉向的功能。

  1. 最後一種方案是採用遞迴的方式進行連結串列反轉。這種方式也需要一定的理解。我們先展示一下程式碼。
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中方法。除此之外還想寫一些連結串列的簡單操作。

快慢指標

何為快慢指標,即對連結串列進行兩個不同步頻的指標標記遍歷。最經典的是慢指標走一步,快指標走兩步。
快慢指標有很多的應用,比如說:

  1. 判斷一個連結串列是否存在環
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進行判斷。

  1. 輸出連結串列中的倒數第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

這是關於連結串列的兩種比較簡單的操作,反轉和快慢指標。挺常見的面試題,在這裡做一些記錄分享。