Day04 链表2

 24. 两两交换链表中的节点 

一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。

# 设置虚拟节点试图统一链表里的操作,虚拟节点的设置方法是?

        dummy_head=ListNode(next=head)

        current=dummy_head

遇到了问题,想知道更新的操作,

在当前节点指向的需要交换的节点,current.next和current.next.next都存在的时候,

交换后第一个节点要指向第二个节点的next,current要指向第二个节点,第二个节点要指向起一个节点

这个顺序怎么来,

ListNode temp = current.next

current.next=current.next.next

current.next.next=current.next.next.next   话说存在吗?指到空怎么办?

current.next.next.next=current.next      (变化了,需要定义一个临时记录的节点temp)

然后移动current到下一对需要交换的节点对的前一个节点  

依旧是错误的代码:

class Solution:
    def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
        # 设置虚拟节点
        dummy_head=ListNode(next=head)
        current=dummy_head  # current指向虚拟头节点  

        # 当 current.next 和 current.next.next  都存在时,进行交换
        while current.next and current.next.next :
            ListNode temp = current.next
            ListNode temp1 = current.next.next.next

            current.next=current.next.next
            current.next.next=temp  #话说存在吗?指到空怎么办?
            temp.next=temp1     #变化了,需要定义一个临时记录的节点temp        

            current=temp

        return dummy_head.next

要注意原来第一个节点dummy到节点1,节点2到3的指向断了,导致节点1、节点3需要保存。

但是一直报错,完全不理解,哪位友友知道的话麻烦留言QAQ

有同志告诉我current.next=current.next.next时,那么temp也被修改了(因为他等于current.next)

current 时,应该更新为 current.next.next,因为当前 current.next 已经被交换到了后面,我们需要移动到下一对需要交换的节点对的前一个节点。temp是会修改的,所以不应该使用。

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  # current指向虚拟头节点  
  
        # 当 current.next 和 current.next.next 都存在时,进行交换  
        while current.next and current.next.next:  
            first = current.next  # 第一个节点  
            second = current.next.next  # 第二个节点  
  
            # 更新指针以交换节点  
            first.next = second.next  # 第一个节点的next指向第二个节点的next  
            current.next = second  # current的next指向第二个节点  
            second.next = first  # 第二个节点的next指向第一个节点  
  
            # 移动current到下一对需要交换的节点对的前一个节点  
            current = current.next.next  
  
        return dummy_head.next

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

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

想到了双指针,先向前移动n-1步,然后两个指针同时移动,直到先行的指针到达链表的末尾

话说链表的长度可以计算吗,关于链表的长度,确实需要确保链表的长度大于或等于n,否则删除倒数第n个节点就没有意义,但是这个要遍历才能求的长度?好像

错误记录:

1、创建了新的ListNode对象,并传递了head作为参数。

right=ListNode(head)

left=ListNode(head)

2、忘了考虑删除的是头节点的情况,如果不使用虚拟节点,需要特别处理头节点的情况。在初始化leftright指针时,直接将它们指向链表的头节点head。然而,当需要删除的是头节点时,这会导致一些问题,因为left指针会指向None,在尝试删除节点时会出现错误。所以要初始节点

综上两点,初始化:

      # 使用虚拟节点简化头节点的删除情况  
        dummy_head = ListNode(0)  
        dummy_head.next = head  
          
        right = dummy_head  
        left = dummy_head  

每次都在想不到的地方报错,这次又是为了啥?修改了成了 if right!=None:

class Solution:
    def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
        # 使用虚拟节点简化头节点的删除情况  
        dummy_head = ListNode(next=head) 

        right = dummy_head  
        left = dummy_head  
        # 先让right指针向前移动n+1步
        for i in range(1+n):
            if right!=None:
                right=right.next
 
        while right:
            right=right.next
            left=left.next

        left.next=left.next.next

        return dummy_head.next

 面试题 02.07. 链表相交  

两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。

说实话,看图之前,我对链表相交这个概念都很困惑,可以部分相交吗?后来想想,链表移动会指向下一个,相交就是一定在某个点指向同一个点,然后相交到末尾,直到None。

这个可以怎么做?应该是尾部对齐后才好比较,话说求链表长度还得先遍历啊。

这题需要虚拟节点吗?如果第一个节点就相交要考虑吗?不过不删除总感觉虚拟节点用处不大。

写的初版,超出时间限制

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        # 从某个点开始相交,直到末尾,如果相交,从尾部开始倒数第n个开始相交,n>=1
        #  遍历分别求链表长度

        curA, curB = headA, headB
        nA,nB = 0,0
        while curA:
            curA=curA.next
            nA+=1
        while curB:
            curB=curB.next
            nB+=1
        # 重新指向开头
        curA,curB = headA,headB
        # 得到长度差
        diff = abs(nA-nB)
        #-------错误的--------
        if nA<nB:
            #  B先向前移动(nB-nA)个
            for i in range(diff):
                curB=curB.next
                while curA and curB:  
                    if curA == curB:  
                        return curA  
                    curA = curA.next  
                    curB = curB.next 

        else:
            #  A先向前移动(nA-nB)个
            for i in range(diff):
                curA=curA.next
                while curA and curB:  
                    if curA == curB:  
                        return curA  
                    curA = curA.next  
                    curB = curB.next
        #-------修正的-------- 
        if nA<nB:
            #  B先向前移动(nB-nA)个
            for i in range(diff):
                curB=curB.next
            while curA and curB:  
                if curA == curB:  
                    return curA  
                curA = curA.next  
                curB = curB.next 

        else:
            #  A先向前移动(nA-nB)个
            for i in range(diff):
                curA=curA.next
            while curA and curB:  
                if curA == curB:  
                    return curA  
                curA = curA.next  
                curB = curB.next              

        return None

这里面正确的实现应该先求出两个链表的长度差,并让长的链表先走这个长度差,然后再同时遍历两个链表,直到找到交点或到达链表末尾。

在原始代码中,for循环用于让较长的链表先移动diff步,但while循环被错误地嵌套在for循环内部。这意味着每次for循环迭代一次,内层的while循环就会完全执行一次,这并不是我们的目的。我们的目的是让长链表先移动diff步后,再同时遍历两个链表以寻找交点。

修改下也可以成功,但是是有点不清晰,改进代码如下:

class Solution:  
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:  
        # 初始化两个指针  
        curA, curB = headA, headB  
          
        # 遍历分别求链表长度  
        nA, nB = 0, 0  
        while curA:  
            curA = curA.next  
            nA += 1  
        while curB:  
            curB = curB.next  
            nB += 1  
          
        # 重新初始化指针指向链表开头  
        curA, curB = headA, headB  
          
        # 得到长度差  
        diff = abs(nA - nB)  
          
        # 让长的链表先走diff步  
        if nA > nB:  
            for _ in range(diff):  
                curA = curA.next  
        else:  
            for _ in range(diff):  
                curB = curB.next  
          
        # 此时两个链表剩余部分长度相同,或者其中一个链表已经遍历完  
        # 同时遍历两个链表,直到找到交点或到达链表末尾  
        while curA and curB:  
            if curA == curB:  
                return curA  
            curA = curA.next  
            curB = curB.next  
          
        # 如果没有交点,返回None  
        return None

话说还可以这样,交换

        if lenA > lenB:     # 让curB为最长链表的头,lenB为其长度
            curA, curB = curB, curA
            lenA, lenB = lenB, lenA 
        for _ in range(lenB - lenA):  # 让curA和curB在同一起点上(末尾位置对齐)
            curB = curB.next 

142.环形链表II

给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

Ps.不理解这里想说啥:评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。输入里也没有啊?

环的话,使用快慢指针来判断链表是否有环,并求解环的入口节点。使用两个指针,一个慢指针每次移动一步,一个快指针每次移动两步。如果链表中存在环,那么两个指针最终会相遇在环中的某个节点上。

假设环的入口到链表头部的距离为x,环内快慢指针相遇时慢指针移动的距离为y,环内从相遇的地点到环的入口的距离为z。快指针的速度是慢指针的两倍。

在快慢指针相遇时,快指针走过的距离是慢指针的两倍。因此,我们有:

快指针走过的距离 = 慢指针走过的距离 × 2
(x + y) × 2 = x + y + n × 环的周长,其中n是快指针在环内绕的圈数。

环的周长可以表示为y + z(从快慢指针相遇点到环的入口,再加上从环的入口到快慢指针相遇点)。由于慢指针在相遇时已经走了y的距离,所以快指针绕了(y + z) × n的距离。

将环的周长代入上面的等式中,我们得到:
(x + y) × 2 = x + y + n × (y + z)

我们让慢指针回到头节点,并将快慢指针的速度都调整为每次一步

慢指针从头节点开始,到达环的起始节点需要走 x 步。快指针从相遇点开始,它首先需要走 z 步到达环的起始节点,然后它可能在环里多走几圈,但每多走一圈都会再次回到环的起始节点。

又x=(n-1)x(y+z)+z,所以,这种情况下(我们让慢指针回到头节点,并将快慢指针的速度都调整为每次一步),两个指针将在入口处相遇。

数学分析结束了,代码实现:

class Solution:
    def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:

        if not head or not head.next:  
            return None  # 如果链表为空或只有一个节点,则一定没有环 

        fast,slow=head,head
        # 快慢指针第一次相遇
        while fast and fast.next:
            fast=fast.next.next  # 话说要注意fast.next是否存在
            slow=slow.next

            if fast==slow:  # 相遇则跳出循环
                break

            # 没有环
            if fast==None:
                return None
        #
        slow=head

        while slow!=fast:
            slow=slow.next
            fast=fast.next
            
        return slow

  • 23
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值