代码随想录算法训练营第四天

本文介绍了四个链表相关的算法问题:两两交换链表中的节点、删除链表的倒数第N个节点、判断链表相交以及寻找环形链表的起点。解题策略包括使用辅助节点、计算链表长度和双指针法。对于环形链表,通过快慢指针确定环的存在并找到起点。
摘要由CSDN通过智能技术生成

代码随想录算法训练营第四天| 24. 两两交换链表中的节点,19.删除链表的倒数第N个节点,链表相交,142.环形链表II

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

题目链接:两两交换链表中的节点
这个题想起来还比较容易的,就是交换节点,但是写代码的时候还是很容易绕不出去,我感觉还是少出现cur.next.next.next会很乱,然后调整的时候也可以把后面的链条存下来去操作。

class Solution:
    def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
        node = ListNode(next=head)
        cur = node
        while cur.next and cur.next.next:
            tmp = cur.next
            post = cur.next.next

            tmp.next = post.next
            post.next = tmp
            cur.next = post

            cur = cur.next.next
        return node.next

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

题目链接:删除链表的倒数第N个节点
这题我自己的做法是直接把倒数第几个算成正数第几个,然后用昨天的删除元素来做,所以先算了一共有几个元素,然后再算正数。

class Solution:
    def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
        def long(head):
            l = 0
            while head:
                l += 1
                head = head.next
            return l
        
        l = long(head)
        dummy_head = ListNode(next=head)
        cur = dummy_head
        num = l-n
        for _ in range(num):
            cur = cur.next
        cur.next = cur.next.next
        return dummy_head.next

链表相交

题目链接:链表相交
=。=这道题想的太多了,没考虑到他自己的特性,最后都准备暴力遍历了,但是没有必要,可以看一下这个题的特点,两个链表相交之后的那一段到结尾就是一样的了,所以完全去想长的那段怎么等短的那段,完全没有必要。
关于例子:[4,1,8,4,5]和[5,0,1,8,4,5]的相交点为什么是8不是1。因为相交不是数值相同,而是指针相同(指向同一位置且指针本身地址相同),4.next != 0.next
在这里插入图片描述
直接让他们在尾巴上对齐了从红线开始一起next知道找到(最后一个)就可以了。
还有一个点是谁长谁短,也不用管,直接任命长的叫A短的叫B就行。

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        def long(head):
            l = 0
            while head:
                l += 1
                head = head.next
            return l
        
        lA = long(headA)
        lB = long(headB)
        # 这里不用管谁是长的谁是短的,直接规定A是长的那个
        if lA > lB:
            d = lA - lB
            curA = headA
            curB = headB
        else:
            d = lB - lA
            curA = headB
            curB = headA
        
        for _ in range(d):
            curA = curA.next
        
        while curA and curB:
            if curA == curB:
                return curA
            else:
                curA = curA.next
                curB = curB.next
            
        return None

142.环形链表II

题目链接:环形链表II
题目解析参考这个视频,卡哥讲的真的特别好。
首先就是把这个题分成两部分:

  1. 链表里有没有环
  2. 环的起点在哪里

链表里有没有环

这个问题用的是双指针,可以做成一个追击问题,一个fast指针和一个slow指针,fast指针每次走两个节点,slow指针每次走一个节点,在没有环的情况下,fast到达结尾None,无法相遇。

在有环的情况下,如果按照相对运动来说,在slow相对静止的情况下,fast以每次一个节点的速度靠近slow,这也是为什么他们一定会相遇的原因。
这里如果fast以三个节点的速度往前的话,对slow来说就是两个节点的相对运动,那就有可能会直接越过,不会相遇。
追击设定

环的起点在哪里

Notation:x = 从head到环的起点;y = 环的起点到相遇位置;z = 相遇位置到环的起点。
fast和slow同时从head出发,fast先进入环内,继续往前走,此时的slow还在直线上(应该是想x/2的位置),然后slow走到环的起点,此时的fast应该在环内的某个位置(可能已经在环里转了几圈了)。
依然是从相对运动的角度来说,slow相对禁止停在环的起点,fast以一个节点的速度去靠近slow,那么fast将走过一部分的环回到环的起点遇到slow,而原始设定下slow就是以一个节点为速度运动,fast以两个节点为速度运动,也就是说slow走过了刚刚fast在相对运动中走过的那一部分环的距离就可以和fast相遇,这就是为什么slow和fast在一圈之内一定会相遇的原因。
接下来就可以写一个式子:

  • slow: 1 × t = x + y 1 \times t = x+y 1×t=x+y
  • fast: 2 × t = x + y + n ( y + z ) 2 \times t = x+y+n(y+z) 2×t=x+y+n(y+z) (n是fast转过的圈数)
  • 最后得到: 2 × ( x + y ) = x + y + n ( y + z ) 2 \times (x+y)= x+y+n(y+z) 2×(x+y)=x+y+n(y+z)
  • 整理一下: x + y = n ( y + z ) → x = ( n − 1 ) ( y + z ) + z x+y=n(y+z) \rightarrow x = (n-1)(y+z)+z x+y=n(y+z)x=(n1)(y+z)+z

首先我们先来看 n = 1 n=1 n=1的情况,也就是 x = z x=z x=z,这个是式子可以解释为,如果有两个指针, i n d e x 1 index_1 index1 i n d e x 2 index_2 index2 i n d e x 1 index_1 index1从head, i n d e x 2 index_2 index2从相遇点以同一个速度出发,他们将在环的起点相遇。
那么当n取别的值时,情况是一样的, i n d e x 1 index_1 index1从head, i n d e x 2 index_2 index2从相遇点以同一个速度出发, i n d e x 1 index_1 index1走直线到环的起点, i n d e x 2 index_2 index2转了 n − 1 n-1 n1圈最后也到了环的起点,因为距离相等他们最终相遇。
写一下代码(感觉这个题还挺浪漫的):

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

class Solution:
    def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
        fast = head
        slow = head
        while fast and fast.next: #fast每次走两个节点所以next也管
            fast = fast.next.next
            slow = slow.next
            if fast == slow:
                index2 = fast
                index1 = head
                while index1 != index2:
                    index1 = index1.next
                    index2 = index2.next
                return index1
        return None
代码随想录算法训练营是一个优质的学习和讨论平台,提供了丰富的算法训练内容和讨论交流机会。在训练营中,学员们可以通过观看视频讲解来学习算法知识,并根据讲解内容进行刷题练习。此外,训练营还提供了刷题建议,例如先看视频、了解自己所使用的编程语言、使用日志等方法来提高刷题效果和语言掌握程度。 训练营中的讨论内容非常丰富,涵盖了各种算法知识点和解题方法。例如,在第14天的训练营中,讲解了二叉树的理论基础、递归遍历、迭代遍历和统一遍历的内容。此外,在讨论中还分享了相关的博客文章和配图,帮助学员更好地理解和掌握二叉树的遍历方法。 训练营还提供了每日的讨论知识点,例如在第15天的讨论中,介绍了层序遍历的方法和使用队列来模拟一层一层遍历的效果。在第16天的讨论中,重点讨论了如何进行调试(debug)的方法,认为掌握调试技巧可以帮助学员更好地解决问题和写出正确的算法代码。 总之,代码随想录算法训练营是一个提供优质学习和讨论环境的平台,可以帮助学员系统地学习算法知识,并提供了丰富的讨论内容和刷题建议来提高算法编程能力。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [代码随想录算法训练营每日精华](https://blog.csdn.net/weixin_38556197/article/details/128462133)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值