作为经典题目的《环形链表Ⅱ》,我认为这题还是有专门出一期博客的分量的,大家也可以自己事先按照自己的理解写一写,然后再来看题解,毕竟自己悟出来的东西是比吸收别人的来的更深刻。

一、题目

  • 给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
  • 如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
  • 不允许修改 链表。

二、思路

  • 首先要确定是否带环
  • 再找入环口

确定不带环很好写,设置一个指针,只要指向空了,那就一定是不带环的,那带环该怎么办呢?

示例:

力扣《环形链表Ⅱ》超详细题解_链表

如图所示,这是一个带环的链表,我们可以设置快慢指针来做这题,快指针每次走两步,慢指针每次走一步,发挥你的空间想象力,假设两个指针都进环了,那快指针每次都比慢指针多走一步,总会慢慢追上慢指针的。

上面是确定带环的思路,好了,现在是怎么找入环口,那怎么找呢?这就要有点数学底子了,先找关系,设快指针的路程为X1慢指针的路程为X2,我们可以把入环前面的路程设为X,从入环口到相遇的位置设置为a,从相遇的位置到入环口为b,如图所示:

力扣《环形链表Ⅱ》超详细题解_开发者_02

剩下的就是找关系了

  • 先看X1X1 = X + n *(a + b) + an代表走了几圈。再看X2X2 = X + a这个就更容易理解了。
  • 再找X1X2的关系:因为快指针每次是走的两步,慢指针每次一步,所以他们两个是二倍的关系,那就可以联立方程组了:
  • 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) + bn表示圈数,先考虑一圈的情况,也就是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;
}

四、运行结果:

力扣《环形链表Ⅱ》超详细题解_快慢指针_03

内存和时间大家图一乐就行,内行人dddd(懂的都懂)

五、总结

总的来说,力扣《环形链表Ⅱ》虽然是一道难度适中的题目,但是在实际编码过程中还是有一些需要注意的细节,比如如何判断是否存在环、如何找到环的起点等。通过本篇超详细题解的阅读,相信大家已经对这个问题有了一个更加清晰的认识。

六、结语

编程之路,不仅仅是一道又一道题目的挑战,更是一场修炼自我的过程。力扣《环形链表Ⅱ》这道题目,虽然难度不高,但却需要我们在实战中不断探索,不断优化解法。编程之路亦是如此,唯有持之以恒、不断努力,才能够在道阻且长的路途中不断前行,逐渐成为一名优秀的程序员。相信无论你是初学者还是有一定经验的开发者,在不断攀登编程高峰的同时,也能够从力扣的各种题目中汲取营养,不断提升自己的技能水平。

力扣《环形链表Ⅱ》超详细题解_链表_04