代码随想录刷题 Day6

0 阅读3分钟

两两交换链表中的节点

这道题最好还是用虚拟头节点法,因为两两交换的时候头节点的位置也要变。不用虚拟头节点,需要单独维护头节点(head)的位置

# 正确解法
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next
class Solution:
    def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
        dummy_head=ListNode(next=head)
        current=dummy_head

        while True:
            if current==None or current.next==None or current.next.next==None:
                break
            else:
                tmp1=current.next
                tmp2=current.next.next.next

                current.next=current.next.next
                current.next.next=tmp1
                current.next.next.next=tmp2

                current=current.next.next
        return dummy_head.next

这题需要画图,不然搞不清楚交换的顺序,还有谁的地址需要缓存。交换顺序具体可以参考代码随想录的文档。

删除链表的倒数第N个节点

反复向后试探

# 我的思路
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next
class Solution:
    def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
        dummy_head=ListNode(next=head)
        current=dummy_head
        judge=current

        while True:
            for _ in range(n+1):
                judge=judge.next
            if judge==None:
                break
            current=current.next
            judge=current
        current.next=current.next.next
        return dummy_head.next

我的思路是反复向后试探:judge往后移动n步,如果juege为None,则说明current已经到了倒数第n个节点了。因为需要删除倒数第N个节点,所以我需要current位于倒数第n+1个节点。

这种做法没错,但是时间复杂度较高,最坏的情况是o(L^2)。利用快慢指针的思想,可以把复杂度降低到o(L)

快慢指针

class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next
class Solution:
    def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
        dummy_head=ListNode(next=head)
        current=dummy_head
        judge=current
        for _ in range(n+1):
            judge=judge.next

        while judge!=None:
            current=current.next
            judge=judge.next
        current.next=current.next.next
        return dummy_head.next

judge 初始化为 current 向后 n+1 个节点。judge 和 current 一起向后移动,当 juege 移动到 None 时,current 刚好指在倒数第 n+1 个节点上

链表相交

暴力循环

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        currentA=headA
        currentB=headB

        while(currentA!=None):
            while(currentB!=None):
                if(currentA==currentB):
                    return currentA
                else:
                    currentB=currentB.next
            currentA=currentA.next
            currentB=headB
        return None

找链表相交的点,其实就是找地址相等的点。最简单的方法就是双重循环来做,时间复杂度最坏的情况是o(mn),m是A链表的长度,n是B链表的长度。力扣有时间限制,不让这么做。

双指针解法

这个解法很巧妙,这道题有一个难点就是A,B的非公共部分的链表长度可能不一致,如果两个指针一起往后移动的话,没法同时到达交点。但是A走完走B,B走完走A,最后就可以让两个指针同时到达交点的位置

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        currentA=headA
        currentB=headB
        while(currentA!=currentB):
            currentA=headB if currentA==None else currentA.next
            currentB=headA if currentB==None else currentB.next
        return currentA

环形链表

这道题有难度,自己想是很难想出来的,需要把解法作为经验积累

class Solution:
    def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
        if head==None or head.next==None: #1. 链表是单节点或者空节点,一定没有环 2. 先判断head是否为空,再判断head.next是否为空,否则空列表没法运行head.next
            return None
        fast=head
        slow=head
        loop_exist=False
        while fast!=None and fast.next!=None : #需要fast和fast.next都不为空,才能访问fast.next.next
            fast=fast.next.next
            slow=slow.next
            if(fast==slow):
                loop_exist=True
                break
        if loop_exist==False:        
            return None
        else:
            find=head
            while(find!=slow):
                find=find.next
                slow=slow.next
            return find

主要是两步:

  1. 判断是否存在循环(利用快慢指针,fast每次向后移动两位,slow每次向后移动一位),如果存在环,则 fast 一定会追上 slow。这个类似于在操场跑步,快的总会追上慢的
  2. 找到环的入口:find指针从链表的头节点向后,slow从fast,slow相遇的点向后移动,fine和slow相遇的点就是入口。具体的推理过程可以看代码随想录

programmercarl.com/algo/linked…