阅读 80

算法解析:查找链表环结构的入口结点

查找链表环结构的入口结点

上吴师兄的算法课,刷leetcode时,遇到了 '查找链表环结构的入口结点'这题.

看完 leetcode 的 '快慢指针,两次赛跑'(我取的名) 的解法时,一脸懵逼.

结合 吴师兄给的 示例动图/解析, 并 划水整整研究一上午后,

(注:此时作者处于,当前迭代末期,活相对较少,好学生请不要模仿)

总结出了这版 文字纯享,无需公式,让你的直觉接受这种设定! 版 算法解析

大家可以先直接去做做原题

AlgoMooc:环形链表2

力扣:环形链表2 142题

题干

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

为了表示给定链表中的环,

我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始).

如果 pos 是 -1,则在该链表中没有环。

注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。

懵逼树上懵逼果,懵逼树下你和我

看到这题,第一反应是 在结点上做标记,路过一个结点,标记一下.

当然,此时leetcode给出了更优的set储存解法,不影响原本数据.

此处也略提一下,理解简单

// 我想的是在节点上做标记,显然用set更好,记住这思路 var detectCycle = function(head) {     const visited = new Set();     while (head !== null) {         if (visited.has(head)) {             return head;         }         visited.add(head);         head = head.next;     }     return null; }; 复制代码

重点

此时还给出了第二个更优解法 空间复杂度为O(1)

诠释了什么叫 我不理解,但我大受震撼.jpg

这里直接讲 解法 '快慢指针,两次赛跑'

思路是,

第一次, 使用 快慢指针, 快指针每次移动2节点, 慢指针每次移动1节点.

当 快指针与慢指针 第一次相遇时, 当前节点 称为 相遇点.

第二次, 使用 两个慢指针, 慢指针1 从 相遇点出发, 慢指针2 从 节点头出发,

当 两个慢指针 第一次相遇时, 此时相遇节点 即为 环入口点.

无需公式,看完后,让你的直觉接受这种设定!

话不多说,直接上解析,代码在后面

// 要理解 快慢指针 找链的环结构入口,要理解两个重要结论, // 1.慢指针 走过的的长度 等于 环的长度 // 2.慢指针 从头走到环入口的长度 等于 慢指针 从相遇点走完剩余环的长度 // 假设 快指针 一次走 2个节点,慢指针 一次走 1个节点 // 假设 头到环入口长度 a, 环长度 b. // 第 1 次相遇时, 快指针总路程 f,慢指针总路程 s,此时可得到两个等式 // (注意是第1次相遇,如果继续跑,第1次相遇后,s每跑1圈,f会跑2圈,会再次在这相遇,算法只需要考虑第1次相遇就行) // f(快指针路程) = 2s(慢指针走的路程) (快指针是慢指针两倍速) // f(快指针路程) = s(慢指针走的路程) + b(环的长度) // 得到 s = b (慢指针走的路程 就是 环的长度b) // 可理解为,第一次相遇的时候,s还刚进 第1圈,f已经多走了1圈环,所以 s=b // 所以得到 重要结论 (1) 慢指针路程 s = b 环的节点数 // 接下来要思考,直觉上来说,为什么再次从头开始走一个 慢指针2 // 同时从第一次相遇处 走 慢指针1,两个慢指针必然会在 环入口处相遇? // 换句话说 为什么 慢指针1 走完 剩余环的长度(y),会等于 从头到入环 的长度? // 此时,我们整理下, 头到环入口a, 环入口到相遇处(x), 环长度b // 很显然 环的长度(b) = x(入口到相遇处) + y(相遇处到环入口) // 同时由结论(1) 环的长度(b) = s(慢指针路径) = a(头到环入口) + x(入口到相遇处) // x(入口到相遇处) + y(相遇处到环入口) = a(头到环入口) + x(入口到相遇处) // 语言解释就是, 头到相遇处, 相遇处再到环入口,是等长的,重合部分就是 入口到相遇处 // 路径上来说,都减去重合部分(入口到相遇处x),剩余部分路径相等 // 如果 假设两个慢指针,都不走重合部分,一个从头开始走,一个跳过重合部分从相遇处开始走 // 相同速度,走过相同路径时,便会相遇在入口 var detectCycle = function(head) {     if (head === null) {         return null;     }     let slow = head, fast = head;     while (fast !== null) {         slow = slow.next;         if (fast.next !== null) {             fast = fast.next.next;         } else {             return null;         }         if (fast === slow) {             let ptr = head;             while (ptr !== slow) {                 ptr = ptr.next;                 slow = slow.next;             }             return ptr;         }     }     return null; }; 复制代码

总结

如有疵漏,大佬轻喷,同时感谢指点.

如果这篇文章能帮到大家,就是我最大的快乐!!!


作者:Lazy宇
链接:https://juejin.cn/post/7019137072975839263


文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐