【leetcode】鏈表問題

題目1

【簡單】環形鏈表

給定一個鏈表,判斷鏈表中是否有環。

爲了表示給定鏈表中的環,我們使用整數 pos 來表示鏈表尾連接到鏈表中的位置(索引從 0 開始)。 如果 pos 是 -1,則在該鏈表中沒有環。

 

示例 1:

輸入:head = [3,2,0,-4], pos = 1
輸出:true
解釋:鏈表中有一個環,其尾部連接到第二個節點。

示例 2:

輸入:head = [1,2], pos = 0
輸出:true
解釋:鏈表中有一個環,其尾部連接到第一個節點。

 

 

解答

快慢雙指針,可以有效的減少空間複雜度和時間複雜度的匹配次數

首先,由於鏈表是個環,所以相遇的過程可以看作是快指針從後邊追趕慢指針的過程。那麼做如下思考:
1:快指針與慢指針之間差一步。此時繼續往後走,慢指針前進一步,快指針前進兩步,兩者相遇。
2:快指針與慢指針之間差兩步。此時繼續往後走,慢指針前進一步,快指針前進兩步,兩者之間相差一步,轉化爲第一種情況。
3:快指針與慢指針之間差N步。此時繼續往後走,慢指針前進一步,快指針前進兩步,兩者之間相差(N+1-2)-> N-1步。

因此,此題得證。所以快指針必然與慢指針相遇。又因爲快指針速度是慢指針的兩倍,所以相遇時必然只繞了一圈。

上述解釋來自鏈接:https://www.zhihu.com/question/23208893/answer/117115415
 

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def hasCycle(self, head: ListNode) -> bool:
        # 快慢雙指針追趕碰撞解法,時間複雜度爲O(n),空間複雜度爲O(1)
        if not head:
            return head
        slow = head
        quick = head
        while quick and slow:
            # 這裏因爲quick是跳兩次,所以要判斷quick和quick.next是否都爲空,否則會報NoneType的異常
            slow = slow.next
            if quick.next:
                quick = quick.next.next
            else:
                return False
            if quick is slow:
                return True
        return False

快慢指針的用途:

1、判斷鏈表中的環(如上題所示)

2、找中間值

一般的思路是:先遍歷一次鏈表,記錄住一共有多少個節點,然後,再次遍歷找尋中點。

利用快慢指針,我們來看看這個問題會變成什麼樣。思路如下:我們把一個鏈表看成一個跑道,假設a的速度是b的兩倍,那麼當a跑完全程後,b剛好跑一半,以此來達到找到中間節點的目的。

如下圖,最開始,slow與fast指針都指向鏈表第一個節點,然後slow每次移動一個指針,fast每次移動兩個指針。

3、刪除倒數第n個節點

刪除倒數第n個節點,那就等於是要我們先找出待刪除元素前一個元素,也就是第n-1個節點。聰明的你肯定發現了,我們又把這個問題轉化爲找鏈表上的某個節點的問題了,這是快慢指針最擅長的場景。

那如何找第(n-1)個元素呢?我們一開始就讓fast指針比slow指針快n+1個元素,接下來,兩個指針都是一步一步來往下走。那麼當fast指針走完時,slow指針就剛剛好停留在第(n-1)個元素上。

以下圖解了n=2時的情形(其中dummyHead是我們手動加上去的虛擬頭節點):

 

題目2

【簡單】迴文鏈表

編寫一個函數,檢查輸入的鏈表是否是迴文的。

 

示例 1:

輸入: 1->2
輸出: false

示例 2:

輸入: 1->2->2->1
輸出: true

 

進階:
你能否用 O(n) 時間複雜度和 O(1) 空間複雜度解決此題?

 

解答

  1. 快慢指針遍歷到鏈表中間,快指針走兩步、慢指針走一步,最後慢指針的位置就是鏈表中間
  2. 從中間開始反轉鏈表後半段
  3. 從原鏈表頭和反轉後的鏈表頭開始比較 value
# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def isPalindrome(self, head):
        if not head: 
            return True
        slow = head
        fast = head

        # slow 遍歷到中間,最後 slow 停的位置是 n/2+1
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
        
        # 然後從 slow 開始反轉鏈表
        # 如果是奇數,多出來的一個節點不用管了
        pre = slow
        while slow and slow.next:
            tmp = slow.next.next
            slow.next.next = pre
            pre = slow.next
            slow.next = tmp

        # 反轉後的頭結點是 pre,從 pre 和 head 開始比較 val
        while head and pre:
            if head.val != pre.val:
                return False
            head = head.next
            pre = pre.next
        
        return True


# 鏈接:https://leetcode-cn.com/problems/palindrome-linked-list-lcci/solution/shuang-zhi-zhen-fan-zhuan-lian-biao-python-by-yuyi/

 

 

【參考】

鏈表中快慢指針的妙用

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章