代码随想录Day4

@代码随想录算法训练营

代码随想录Day4

链表理论基础 24. 两两交换链表中的节点 ,707.设计链表,206.反转链表

2024/03/24 今日任务:
完成英语每日阅读+听力
答辩PPT - 搞不定就粗略写点东西算了,周一再精修
完成Day4的算法代码学习
路由技术卷2,15页还没看 - 累积到42页了,想办法今天清理一下,压到30页以内

Day4题目

1、24.两两交换链表中的节点
题目链接:https://leetcode.cn/problems/swap-nodes-in-pairs/description/
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
示例1:
输入:head = [1,2,3,4]
输出:[2,1,4,3]

示例 2:
输入:head = []
输出:[]

示例 3:
输入:head = [1]
输出:[1]

提示:
链表中节点的数目在范围 [0, 100] 内
0 <= Node.val <= 100

解题:还是虚拟节点好用。习惯对虚拟节点添加之后,能够找到一个固定的公式化处理方式。两两节点交换主要是依赖流程的设计。dummyNode->Node1->Node2->Node3
大概想象一下节点交换的时候,对节点2和节点1先进行处理交换。

  • 其实对节点进行处理的话,无可避免就是会断链,因此,最无脑处理的就是考虑哪个Node会被断链,那么就对谁进行temp保存。
  • 通过画图容易知道Node2到Node3以及dummyNode到Node1会被切断,如上链表所示,需要对Node1以及Node3进行保存。那么剩下的就是Node->next的处理。
  • 切记:current指向的节点的next需要非常清晰想象当前的节点是什么,它的next以及next->next的节点是什么,实在没办法想象就纸和笔解决。
    6/8更新
    采用dummyhead的方法,要切记循环不变量的考虑,非常建议画图看清楚到底当前循环不变量遵循的是什么条件,以什么为前提作为循环。
    如以下代码:
    cur是以dummyhead作为基准,推动slow以及fast指针。如果cur,fast,slow不是各负责1个链子节点,那么在替换过程一定会出错/判断过程一定会出错,二者必定会出现1个结果。
    因此,在写不变量的时候,需要清晰考虑,到底更新的是什么值,以多少个单位/什么类型作为更新一次,那么才不会容易出错。
    dummyheader := &ListNode{Next:head}
    cur := dummyheader
    for ;cur.Next != nil && cur.Next.Next !=nil;{
        slow := cur.Next
        fast := cur.Next.Next
        temp := fast.Next
        fast.Next = slow
        slow.Next = temp
        cur.Next = fast
        cur = slow
    }
    return dummyheader.Next

2、19.删除链表的倒数第N个节点
题目链接:https://leetcode.cn/problems/remove-nth-node-from-end-of-list/description/
示例 1:

输入:head = [1,2,3,4,5], n = 2 输出:[1,2,3,5]

示例 2:

输入:head = [1], n = 1 输出:[]

示例 3:

输入:head = [1,2], n = 1 输出:[1]

解法:通常对链表的操作一般都是dummyhead虚拟节点比较好处理。题目给的条件没有太过分,如果加上需要对链表的第N个节点检查是否有效,那么就需要知道长度,但是题目没有要求,所以偏简单一点。

  • 首先,使用两个指针建立窗口,推动窗口滑动来确定fast到slow的距离是2。
  • 然后直接推到fast为nil时,直接对slow.next = slow.next.next即可

题目用到双指针以及固定窗口进行滑动,又是对双指针进行操作,多练练即可。

3、02.07. 链表相交
题目链接:https://leetcode.cn/problems/intersection-of-two-linked-lists-lcci/description/

解法:这道题存在长度差,所以能够利用类似螺旋形的搜索方式,使用双指针不停利用长度差来靠近相等的那一端,直到双指针都为nil为止。具体做法在卡哥、https://programmercarl.com/%E9%9D%A2%E8%AF%95%E9%A2%9802.07.%E9%93%BE%E8%A1%A8%E7%9B%B8%E4%BA%A4.html#go 这里。

func getIntersectionNode(headA, headB *ListNode) *ListNode {
    l1,l2 := headA, headB
    for l1 != l2 {
        if l1 != nil {
            l1 = l1.Next
        } else {
            l1 = headB
        }

        if l2 != nil {
            l2 = l2.Next
        } else {
            l2 = headA
        }
    }

    return l1
}

建议用纸笔模拟一下替换过程才能理解。
那么正常做法也是利用长度差,并且利用了一个机制“intersection”的头节点到尾部的长度是相同的。

意味着有以下特征出现:

  • 两条链表假如相交,则说明相交后的数据是相同的。
  • 相交后的长度也是相同的。
  • 如果从后往前数,那么直到相交前的节点都是相同的。

因此利用了以上的特征,对两个链表从后对齐的形式进行对比,一旦对比到相同指针,那么就能够找到答案,否则,相交点不出现,直到最后两边指针都为nil时,退出比较,因此比较条件就是两个搜索的指针不相同即可。

4、142.环形链表II
题目地址:https://leetcode.cn/problems/linked-list-cycle-ii/description/

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

为了表示给定链表中的环,使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。

说明:不允许修改给定的链表。

解题:
目标

  • 找到环路 (双指针步差)
  • 找到环路入口 (公式推导)

当环路出现的时候且两个指针进入了不停循环的过程时,只要他们存在步差,即往前推进的速度不一致,那么就一定会有重合的那一个情况出现。利用faster比slow快1倍的移动速度,最终一定会进行相遇。

而对于找到环路入口,卡哥有详细讲解。https://programmercarl.com/0142.%E7%8E%AF%E5%BD%A2%E9%93%BE%E8%A1%A8II.html#%E6%80%9D%E8%B7%AF

对走过的路径列方程:(x + y) * 2 = x + y + n (y + z)
能够发现一些比较特殊的点以及相关规律,代入第一个圈即相遇的情况。
n=1时,x=z
即得出以下结论:
“从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点。”
此时,重新建立两个指针,分别跑完x和z后,他们就会相遇,从而找到环的开头。
在这个时候,相遇的情况下就是等于y的距离,即环减去y则相当于剩下z:
环距离 - y = z = x
自此,题目解决。

还有另一种办法:用集合解决,太方便,但是没有办法做到O(1)的空间复杂度:

(版本二)集合法
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None


class Solution:
    def detectCycle(self, head: ListNode) -> ListNode:
        visited = set()
        
        while head:
            if head in visited:
                return head
            visited.add(head)
            head = head.next
        
        return None

比较烧脑,但是想通了会脑洞大开,尽管是看了答案。这一题应该是代码随想录从头到尾的第一条碰到需要列数学公式证明的题目吧,不知道后面还会有多难。。。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值