day4 連結串列-模擬與快慢指標

haohaoscnblogs發表於2024-07-20

目錄
  • 任務
    • 24. 兩兩交換連結串列中的節點
      • 思路
    • 19.刪除連結串列的倒數第N個節點
      • 思路
    • 面試題 02.07. 連結串列相交
      • 思路
    • 142.環形連結串列II
      • 思路
  • 總結

任務

24. 兩兩交換連結串列中的節點

給你一個連結串列,兩兩交換其中相鄰的節點,並返回交換後連結串列的頭節點。你必須在不修改節點內部的值的情況下完成本題(即,只能進行節點交換)。

思路

這是一道模擬的題,修改的指標較多,較複雜,需要弄清楚修改的順序,細心的一步步修改。但是大體的思路還是不變,引入了虛擬節點,注意單次迴圈中的節點指標域變化。

class Solution:
    def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
        dummy = ListNode()
        dummy.next = head
        cur = dummy
        while cur and cur.next and cur.next.next:
            
            oldCurNext = cur.next
            oldCurNextNextNext = cur.next.next.next
            
            cur.next = cur.next.next
            cur.next.next = oldCurNext
            oldCurNext.next = oldCurNextNextNext

            cur =cur.next.next
        return dummy.next

19.刪除連結串列的倒數第N個節點

思路

兩個人同樣的速度勻速跑步,初始時B在A前面n米,此時兩人同時跑步,則B到達終點時,A距離終點也是n米。類比刪除倒數第n個的節點,需要slow和fast指標,fast先走n步,然後slow,fast同時走,則fast到達終點時,slow指向的就是倒數第n個節點。為了刪除該節點,則應該找到待刪除節點的前一個節點的引用,因此fast開始時先走n+1步,此時再按照剛才的邏輯,slow就會指向待刪除節點的前一個節點,修改其指標域即可。此外,該題限制了n的正確性1 <= n <= size,因此不需要邊界處理錯誤的情況。

class Solution:
    def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
        dummy = ListNode()
        dummy.next = head
        slow,fast = dummy,dummy
        while n:
            fast=fast.next
            n-=1
        fast = fast.next
        while fast:
            slow = slow.next
            fast = fast.next

        slow.next = slow.next.next
        return dummy.next 

面試題 02.07. 連結串列相交

給你兩個單連結串列的頭節點 headA 和 headB ,請你找出並返回兩個單連結串列相交的起始節點。如果兩個連結串列沒有交點,返回 null。

思路

與上面刪除倒數第n個節點有點類似,先分別遍歷兩個連結串列,得到長度差異delta,此時知道哪個連結串列長哪個連結串列短,或者相同,類似之前的邏輯,長的先跑delta步,此時兩個一起跑,則第一次遇到時就是它們的相交節點,如果短連結串列都遍歷完了還沒有遇到,說明兩個連結串列不相交。

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        cur = headA
        delta = 0
        while cur:
            cur = cur.next
            delta+=1
        cur = headB
        while cur:
            cur = cur.next
            delta-=1
        longerListPointer = headA if delta >0 else headB
        shorterListPointer = headA if longerListPointer==headB else headB
        delta = abs(delta)
        while delta:
            longerListPointer = longerListPointer.next
            delta-=1
        while shorterListPointer:
            if longerListPointer == shorterListPointer:
                return longerListPointer
            longerListPointer= longerListPointer.next
            shorterListPointer = shorterListPointer.next
        return None

142.環形連結串列II

給定一個連結串列的頭節點 head ,返回連結串列開始入環的第一個節點。 如果連結串列無環,則返回 null。
如果連結串列中有某個節點,可以透過連續跟蹤 next 指標再次到達,則連結串列中存在環。 為了表示給定連結串列中的環,評測系統內部使用整數 pos 來表示連結串列尾連線到連結串列中的位置(索引從 0 開始)。如果 pos 是 -1,則在該連結串列中沒有環。注意:pos 不作為引數進行傳遞,僅僅是為了標識連結串列的實際情況。
不允許修改 連結串列。

思路

  1. 快慢指標相遇判斷是否有環(是否相遇,之所以有環時一定相遇是因為它們的速度差為1個單位,不會發生快指標跳過慢指標的情況)。
  2. 入口節點
  • 方法一:相遇後,讓其中一個節點從頭走,然後另一個節點從當前位置走,再次相遇即是入口節點(需要畫圖用等式證明,不是很好想到,而且暫時沒有理解為什麼快指標追上慢指標至少需要一圈)
  • 方法二:這個方法利用雜湊表來儲存已經訪問過的節點,檢測到第一個重複訪問的節點即為環的入口節點。 (好理解,但空間複雜度O(n))
class Solution:
    def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
        slow,fast = head,head
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
            
            if slow == fast:
                slow = head
                while slow!=fast:
                    slow = slow.next
                    fast = fast.next
                return slow
        return None

總結

今天起的太早出門,狀態很差,抽時間把今天的題再思考和做一下。今天主要學到的有以下幾點。

  • 連結串列中修改指標(插入,刪除)類似題目的核心是,弄清楚一次迴圈中需要修改哪些指標域,以及修改順序。快慢指標可以
  • 快慢指標在連結串列中的應用,可以讓快指標先走n步然後兩個同時走,這裡有個不變數就是它們的delta,此後當到達終點後,就有另一個等式,即終點離slow節點的距離就是n步
    ;另外,快慢指標還可以彌補兩個不同相交連結串列的行走差距
  • 快慢指標在環形連結串列入口節點的應用,後續需要思考下為什麼fast指標至少跑一圈才能追上slow

相關文章