day4 算法训练营-链表part2

题目链接:https://leetcode.com/problems/swap-nodes-in-pairs/

leetcode 24

讲解链接:

代码随想录

看到题目的第一思路:

看起来简单做起来难

代码随想录之后的想法和总结:

因为不是简单的交换数值,而是移动两个节点的物理位置,所以所需要的操作比我想象中多

1遍历的终止条件要注意,cur指针的后一位为空节点或者后两位为空节点都可以停止遍历,因为没有节点需要交换了,注意分链表节点奇数和偶数的情况

2 设置cur指针,是为了方便cur后面两个节点的交换

3 用两个temp指针来保存节点(之前用过的方法)

遇到的困难:

暂无

可以记录备用的固定代码方法模版:

# Definition for singly-linked list.
# 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) #定义虚拟头节点
        cur = dummy_head #cur指针摆放

        while cur.next and cur.next.next:#设置好遍历结束条件
            temp = cur.next #用temp保存好两个节点
            temp1 = cur.next.next.next

            cur.next = cur.next.next#cur指向节点2
            cur.next.next = temp #节点2指向节点1
            temp.next = temp1 #节点1指向节点3
            cur = cur.next.next #交换完节点1和2,该交换3和4之前,先移动cur指针位置往后两位

        return dummy_head.next#返回虚拟头节点的下一位为真正头节点

题目链接:https://leetcode.com/problems/remove-nth-node-from-end-of-list/

leetoce 19

讲解链接:

代码随想录

看到题目的第一思路:

如同前面一题的思路一样,如果要删除某一个节点,那么我们的指针必须要指向被删除节点的前一位,才能确保指针指向被删除节点的下一个节点,从而达到删除节点的目的

代码随想录之后的想法和总结:

双指针应用的经典题目-要掌握思路

评论区看到的很好的回答:

“为啥要让快指针先行?
我认为更好懂的一种解释:快指针先行n步,这样快慢指针之间形成了一段长度为n的窗口,之后快慢指针同步向前相当于保持窗口长度不变。这样当快指针到达了末尾指向NULL,另一端的慢指针距离末尾的长度是n,自然就是指向倒数第n个位置了。
为啥快指针先行了n+1步?

因为只有这样同时移动的时候slow才能指向删除节点的上一个节点(方便做删除操作)

遇到的困难:

暂无

可以记录备用的固定代码方法模版:双指针

# Definition for singly-linked list.
# 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)#定义虚拟头节点
        fast = dummy_head #放置快慢节点在虚拟头结点处
        slow = dummy_head
        
        for i in range(n+1):#让快指针先移动n+1步
            fast = fast.next
            
        while fast:#在fast指针指向null之前,快慢指针同时移动
            fast = fast.next
            slow = slow.next
            
        slow.next = slow.next.next #改变节点指向来删除节点
        
        return dummy_head.next #return头节点


        


题目链接:https://leetcode.com/problems/intersection-of-two-linked-lists/

leetoce 160

讲解链接:

代码随想录

看到题目的第一思路:

消除长度差的方法

代码随想录之后的想法和总结:

注意:交点不是数值相等,而是指针相等

首先求出两个链表长度差,然后让两个链表的末尾对齐,最后移动两个链表的指针直到找到相同的节点指针

最核心的方法-因为是相交节点,所以两个链表相交之后的部分是一样的

并且相交节点一定是位于链表后面部分,所以要消除长度差然后比较后面部分

遇到的困难:

1 为什么要消除长度差?

既然是相交节点,相交节点的后续部分一定完全一致,毕竟相交节点对于两个列表来说就是同一个节点,那么也就是说相交节点以及后续部分一定是位于列表最后部分的。所以只需要把长列表的起点使得该起点到结尾的距离与短列表开头到结尾开头的距离一致,之后同步向后移,只要有相交节点那么必然能同时遇到。

可以记录备用的固定代码方法模版:

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

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]:
        lenA,lenB = 0,0
        cur = headA
        while cur: #求链表A长度
            cur = cur.next
            lenA += 1
        cur = headB #求链表B长度
        while cur:
            cur = cur.next
            lenB += 1
        
        curA, curB = headA,headB #curA,curB分别为两个链表的指针指向各自头节点
        if lenA > lenB: #让curB为最长链表的指针,lenB为长度
            curA,curB = curB,curA
            lenA,lenB = lenB,lenA
        for i in range(lenB-lenA):
        #让较长链表的指针首先移动直到消除长度差,两个链表末尾对齐
            curB = curB.next
        while curA:
         #开始遍历curA和curB,遇到相同的值就停下,否则就继续向后移动直到找到交点
            if curA == curB:
                return curA
            else:
                curA= curA.next
                curB = curB.next

        return None
        #如果遍历完成没有交点就退出返回空指针
        

时间复杂度:

O(n+m), where 𝑛 and 𝑚 are the lengths of the two lists.(时间复杂度就是遍历了两个链表的长度)

时间复杂度为 𝑂(𝑁+𝑀)O(N+M) 的计算是基于以下步骤中各个操作的执行次数:

  1. 遍历两个链表以计算长度:O(N+M)。

  2. 调整链表头部以对齐:如果两个链表的长度不相等,我们需要在较长的链表上移动其头指针,∣N−M∣ 步,这一步虽然也涉及到线性的移动,但它的操作次数被包含在总长度 N+M 中,因为它不会超过链表本身的长度。

  3. 同时遍历两个链表寻找交点:一旦链表对齐,只需遍历较短链表的长度即可(因为长链表的起始点已经调整),这意味着最多遍历 min⁡(𝑁,𝑀) 步。但注意,这不增加总体复杂度,因为整体过程仍然受制于两个链表的总长度 N+M。

空间复杂度:该算法没有使用额外的数据结构,如数组、栈、队列或哈希表,来存储链表节点或其他信息,算法的操作是在原链表上直接进行的,只是通过移动指针来检查节点的相交情况。算法的空间复杂度是 𝑂(1),即常数级别的额外空间。


题目链接:https://leetcode.com/problems/linked-list-cycle-ii/

leetoce 142

讲解链接:

代码随想录

看到题目的第一思路:

暂无

代码随想录之后的想法和总结:

非常巧妙的思路,还有整齐的数学推理思路,是可以常常回来复习的一道题目

最重要的是明白考察重点:1 链表是否有环 2 如果判断有环,那么找到环的入口

判断环:快慢指针,每当慢指针前进一步,快指针前进两步。如果快指针遇到空指针,说明无环;如果快指针遇到慢指针,说明存在环。

计算环所在的位置:当快慢指针相遇时,让其中一个回到头结点,然后同速前进,再次相遇即为环开始的点。

遇到的困难:

1当时卡在不明白为什么需要两个节点-环形入口节点以及快慢指针相遇节点,后来搞明白了虽然有相遇节点证明这个链表有环,但是还是需要找到环形入口节点

2 "为什么第一次在环中相遇,slow的 步数 是 x+y 而不是 x + 若干环的长度 + y 呢?-

快指针速度是慢指针的两倍,慢指针跑一圈的时间快指针能跑两圈,所以慢指针在跑完一圈之前二者一定会相遇

可以记录备用的固定代码方法模版:

# 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]:
        slow = head
        fast = head

        while fast and fast.next:#判断快指针和快指针的下一个不为空,无需判断慢指针
            slow = slow.next
            fast = fast.next.next

            if slow == fast:#如果快慢指针相遇,则说明有环
                slow = head #慢指针从头节点开始
                fast = fast #快指针从相遇节点开始
                #两个指针从头节点和相遇节点,各走一步,直到相遇,相遇点即为环入口
                while slow != fast: 
                    slow = slow.next
                    fast = fast.next
                return slow #return快/慢指针的相遇点(环入口)

        return None #如果没有环,那么return none

时间复杂度:

O(n):快慢指针相遇前,指针走的次数小于链表长度,快慢指针相遇后,两个index指针走的次数也小于链表长度,总体为走的次数小于 2n

今日收获,学习时长:

差不多6h

最后一题比较难,花了较多的理解时间,继续加油keep going

链表总结:(借用代码随想录的图片)

  • 30
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值