省流:数学较好的同学可以直接忽略本文~
先看原题:
原题链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
中等
给定一个链表,返回链表开始入环的第一个节点。 从链表的头节点开始沿着
next
指针进入环的第一个节点为环的入口节点。如果链表无环,则返回null
。为了表示给定链表中的环,我们使用整数
pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果pos
是-1
,则在该链表中没有环。注意,pos
仅仅是用于标识环的情况,并不会作为参数传递到函数中。说明:不允许修改给定的链表。
如题,很容易想到hash表解题,当然这是最简单粗暴的方式,但是别急往下翻
进阶:是否可以使用 O(1)
空间解决此题? 看到这句话的时候是不是心凉了一大截?
别慌先双指针走一波,首先可以想到的是,如果使用快慢指针很容易找到环内的任意一点,即环检测,但是此点并不一定是环起点。所以还可以进一步思考,如果找到此点依次向后检测再使用一指针从起点处开始向右移动到相交处得出距离最短即环起点。
这个思路乍一看好像还有点意思(虽然只有一点点),但是复杂度直接上升为O(mn)而且实现起来肯定是又臭又长,接下来就要走到本文的核心了,也就是尝试用数学思维解决问题。
这里重点谈论的思维也就是该怎么构思这个问题呢?首先要对整个过程进行抽象,想要找到起点那么肯定要先有环也即第一个交点,在这里先简单停顿一下试图在脑子里构建出画面~那么回到问题本身,这恶搞问题实际上有三个要素:
1.距离:起点换一种说法就是从链表头到环的距离。
2.关键点:第一次相交点即环检测点。
3.关系:快慢指针走过的距离的关系。
那么除了相遇之外自然就是边界条件的选择,也就是不相遇的情况这个比较简单,当快指针为null则说明链表有界自然无环直接返回null。 除此之外头节点为null和自身为环也可直接返回。
那么就只剩下大于等于2个节点的情况,实际上上述三要素第三点是最重要的的也就是说问题实际上都是通过构建未知已知所求之间的“关系”构建的,先上图:
1.我们假设快指针是慢指针的2倍速度,那么如果连表有环,快指针会在环中任意一点p追上慢指针。
2.此前提下快指针走过的距离时a+b+n(b+c),n为任意整数为快指针走过的整圈数。
3.慢指针走过距离位a+b
有了上述前提那么就要找出关系,在我们假象状态下唯一已知就是快慢指针关系,根据快慢指针特性可以得出2(a+b) = a+b+n(b+c),因为已知速度2倍的关系,那么在相遇的前提下距离自然也是2倍的关系。
回想上述三要素第一点,我们最后所求实际上就是a的值,那么2(a+b) = a+b+n(b+c)变换得出a=n(b+c)-b。这是个啥没看懂?没关系先记住看不懂说明还有条件没有被挖掘到。
虽然我们不能直接得出a的值因为已知的常量不够,但是我们可以运用“数学思维”,这里的n(b+c)实际上就是一个点在起点来回打转,-b实际上是一个节点走了负的b距离。初次之外我们在位置上唯一的已知时快慢指针位置,且此时指针在p点相交。p距离环起点的距离刚好为b。此时如果慢指针走c个距离正好到达起点,但是c是未知。此时陷入僵局~
再次梳理条件,如果假设一点从某个位置出发假设词典会走L距离,当什么情况下才可使此点和满指针在环起点相遇?两个值(实际上二者等价):
1.L+b=b+c
2.L+b=n(b+c)
此时向上看,a不就正好等于n(b+c)-b那么显然L=n(b+c)-b即可,即此点从起点出发正好和慢指针相遇。所以问题迎刃而解~
看到这里先别急着吐槽(因为观察图形也可以看出这个关系),回顾上文实际上笔者想表达的是一个问题怎么通过构建条件列出关系最后再把关系映射到具体问题上最后得出结论这个过程。也是题目提到的数学思维,图形观察固然可以推断出关系,但是笔者认为这是不牢靠的,换一种场景就很难再得出同样的结论了,问题虽然不难,但是思维的转变仍然需要一点时间。
最后感谢阅读~。好了不说了,笔者要长脑子了。。。