作为经典题目的《环形链表Ⅱ》,我认为这题还是有专门出一期博客的分量的,大家也可以自己事先按照自己的理解写一写,然后再来看题解,毕竟自己悟出来的东西是比吸收别人的来的更深刻。
一、题目
- 给定一个链表的头节点
head
,返回链表开始入环的第一个节点。 如果链表无环,则返回null
。 - 如果链表中有某个节点,可以通过连续跟踪
next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。如果pos
是-1
,则在该链表中没有环。注意:pos
不作为参数进行传递,仅仅是为了标识链表的实际情况。 - 不允许修改 链表。
二、思路
- 首先要确定是否带环
- 再找入环口
确定不带环很好写,设置一个指针,只要指向空了,那就一定是不带环的,那带环该怎么办呢?
示例:
如图所示,这是一个带环的链表,我们可以设置快慢指针来做这题,快指针每次走两步,慢指针每次走一步,发挥你的空间想象力,假设两个指针都进环了,那快指针每次都比慢指针多走一步,总会慢慢追上慢指针的。
上面是确定带环的思路,好了,现在是怎么找入环口,那怎么找呢?这就要有点数学底子了,先找关系,设快指针的路程为X1
慢指针的路程为X2
,我们可以把入环前面的路程设为X
,从入环口到相遇的位置设置为a
,从相遇的位置到入环口为b
,如图所示:
剩下的就是找关系了
- 先看
X1
:X1 = X + n *(a + b) + a
,n
代表走了几圈。再看X2
:X2 = X + a
这个就更容易理解了。 - 再找
X1
和X2
的关系:因为快指针每次是走的两步,慢指针每次一步,所以他们两个是二倍的关系,那就可以联立方程组了: X1 = X + n(a + b) + a
X2 = X + a
X1 = 2X2
所以可得:X + a = n(a + b)
再设圈的长度是N
,可得:X + (N - b) = N * n
,化简得:X = N(n - 1) + b
,n
表示圈数,先考虑一圈的情况,也就是n = 1
,可得:X = b
。 再考虑很多圈的情况,这个方程的意思就是X
的距离,就是圈数再加上b
的距离,那我们可得,不管他走了几圈,反正再多走b
的距离,就是X
的总距离。
所以,思路就有了:我们可以让它们两点在相遇之后,让一个指针重新从起始位置开始走,再次碰头,就是入环口。
三、代码实现
public ListNode detectCycle(ListNode head) {
if (head == null) {
return null;
}
ListNode pk = head;//快指针设置成pk
ListNode pm = head;//慢指针设置成pm
//不仅仅是pk本身不能为空,pk的下一个也不能为空,不然pk的下两个会越界
while ((pk != null) && (pk.next != null)) {
pk = pk.next.next;
pm = pm.next;
if (pk == pm) {
pm = head;
while (pk != pm) {
pk = pk.next;
pm = pm.next;
}
return pm;
}
}
return null;
}
四、运行结果:
内存和时间大家图一乐就行,内行人dddd(懂的都懂)
五、总结
总的来说,力扣《环形链表Ⅱ》虽然是一道难度适中的题目,但是在实际编码过程中还是有一些需要注意的细节,比如如何判断是否存在环、如何找到环的起点等。通过本篇超详细题解的阅读,相信大家已经对这个问题有了一个更加清晰的认识。
六、结语
编程之路,不仅仅是一道又一道题目的挑战,更是一场修炼自我的过程。力扣《环形链表Ⅱ》这道题目,虽然难度不高,但却需要我们在实战中不断探索,不断优化解法。编程之路亦是如此,唯有持之以恒、不断努力,才能够在道阻且长的路途中不断前行,逐渐成为一名优秀的程序员。相信无论你是初学者还是有一定经验的开发者,在不断攀登编程高峰的同时,也能够从力扣的各种题目中汲取营养,不断提升自己的技能水平。