DAY 4 链表part2

目录

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

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

160. 相交链表

解题思路1: 

解题思路2:

141. 环形链表

142. 环形链表 II




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

中等

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

操作两个节点交换时,cur应该指向这两节点的前一个节点 

  • 什么时候终止:
    • 奇数个节点-cur.next.next ==none时结束 
    • 偶数个节点-cur.next ==none时结束 (适用于空链表)
    • 注意⚠️:把cur.next ==none判断写前面,防止对空指针操作

  • 具体如何交换节点:

        1.步骤一直接cur.next = cur.next.next会导致的问题:

修改:先保存节点一temp1= cur.next。同理改变节点二后节点三又获取不到了,保存节点三temp2 = cur.next.next.next

        2.步骤二:cur.next.next = temp1

        3.步骤三:temp1.next = temp2

        4.把cur往后移动两位到节点1位置:cur = cur.next.next

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

            cur.next = cur.next.next #步骤一
            cur.next.next = temp1 #步骤二 #注意这里节点2已经变成cur.next而不是cur.next.next
            temp1.next = temp2 #步骤三
            cur = cur.next.next 
        return dummy.next
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

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

中等

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

算法思想:快慢指针,快指针先移动n步,当快指针的next指向null时,慢指针刚好指向倒数第n个节点的前一个节点

class Solution:
    def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
        dummy= ListNode(next = head)
        right = dummy
        for _ in range(n):
            right = right.next
        left = dummy

        while right.next:
            right = right.next
            left = left.next

        left.next = left.next.next #删除
 
        return dummy.next #return head 示例2会报错,因为head有可能被删除
  • 时间复杂度: O(n)
  • 空间复杂度: O(1)

160. 相交链表

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

图示两个链表在节点 c1 开始相交

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构 。

解题思路1: 

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        lenA, lenB = 0, 0
        cur = headA
        while cur:         # 求链表A的长度
            cur = cur.next 
            lenA += 1
        cur = headB 
        while cur:         # 求链表B的长度
            cur = cur.next 
            lenB += 1
        curA, curB = headA, headB
        if lenA > lenB:     # 让curB为最长链表的头,lenB为其长度
            curA, curB = curB, curA
            lenA, lenB = lenB, lenA 
        for _ in range(lenB - lenA):  # 让curA和curB在同一起点上(末尾位置对齐)
            curB = curB.next 
        while curA:        
 #  遍历curA 和 curB,遇到相同则直接返回。
#如果 curA 和 curB 的相等判断仅基于值(即 curA.val == curB.val),而不是地址(即 curA == curB),那么代码逻辑会错误地认为它们相等。
#正确的做法是确保在比较时,判断的是两个指针是否指向同一个节点。
            if curA == curB:
                return curA
            else:
                curA = curA.next 
                curB = curB.next
        return None 
  • 时间复杂度:O(n + m)
  • 空间复杂度:O(1)

解题思路2:

设「第一个公共节点」为 node ,「链表 headA」的节点数量为 a ,「链表 headB」的节点数量为 b ,「两链表的公共尾部」的节点数量为 c ,则有:

头节点 headA 到 node 前,共有 a−c 个节点;
头节点 headB 到 node 前,共有 b−c 个节点;


考虑构建两个节点指针 A​ , B 分别指向两链表头节点 headA , headB ,做如下操作:

指针 A 先遍历完链表 headA ,再开始遍历链表 headB ,当走到 node 时,共走步数为:a+(b−c)
指针 B 先遍历完链表 headB ,再开始遍历链表 headA ,当走到 node 时,共走步数为:b+(a−c)
此时指针 A , B 重合:
        若两链表 有 公共尾部 (即 c>0 ) :指针 A , B 同时指向「第一个公共节点」node 。
        若两链表 无 公共尾部 (即 c=0 ) :指针 A , B 同时指向 null 。
因此返回 A 即可。

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        A, B = headA, headB
        while A != B:
            A = A.next if A else headB
#如果 A 指向的节点不为 None,则将 A 移动到下一个节点;如果 A 已经到达链表末尾(即 None),则将 A 重置为链表 B 的头节点 headB。
            B = B.next if B else headA
        return A

  • 时间复杂度 O(a+b) : 最差情况下(即 ∣a−b∣=1 , c=0 ),此时需遍历 a+b 个节点。
  • 空间复杂度 O(1) : 节点指针 A , B 使用常数大小的额外空间。

141. 环形链表

简单

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false 。

追击问题,在跑道上走得快的一定会在n圈后追上走的慢的。无环则fast遍历完就会停。

为什么快走两步,慢走一步:相当于慢指针不动,快指针以速度1接近。相对速度为1

跳出循环的条件:while fast and fast.next【fast = fast.next.next的前提条件是fast存在且fast.next存在】

class Solution:
    def hasCycle(self, head: Optional[ListNode]) -> bool:
        low = head
        fast = head
        while fast and fast.next:
            low = low.next
            fast =fast.next.next
            if fast == low:
                return True
        return False 

142. 环形链表 II

中等

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

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

操场跑圈,快的超慢的一整圈

慢指针走的距离是小于环长的:最坏情况,慢指针入环时,快指针在它前面一步,那么以相对速度1分析,快指针走(环长减一)步就能追上,其他情况快指针走的路程都小于(环长减一)-- 一圈之内就能追上。

class Solution:
    def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
        fast = head
        slow = head
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
            if slow is fast:
                while slow is not head:
                    slow = slow.next
                    head = head.next
                return slow
        return None

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值