每日一题:环形链表

88 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第18天,点击查看活动详情

环形链表 |

力扣题目链接

给你一个链表的头节点 head ,判断链表中是否有环。

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

如果链表中存在环 ,则返回 true 。 否则,返回 false 。

示例一:

image.png

输入: head = [3,2,0,-4], pos = 1
输出: true
解释: 链表中有一个环,其尾部连接到第二个节点。

示例二:

image.png

输入: head = [1,2], pos = 0
输出: true
解释: 链表中有一个环,其尾部连接到第一个节点。

示例三:

image.png

输入: head = [1], pos = -1
输出: false
解释: 链表中没有环。

提示:

  • 链表中节点的数目范围是 [0, 104]
  • -105 <= Node.val <= 105
  • pos 为 -1 或者链表中的一个 有效索引 。

解题思路:(双指针)

这道题其实就是要我们判断链表中是否有环,可以用快慢指针来实现

具体思路:

  • 分别定义fastslow指针,同时从头节点出发,fast每次移动两个节点,指针slow每次移动一个节点。
  • 如果slowfast在链表里碰到了,就代表链表里有环,返回true
  • 如果fast走到了null,就代表链表没环,返回false 为什么fast每次移动两个节点,slow每次移动一个节点,有环的时候,指针就一定会在环中碰到了,而不是永远的错开呢:
  • 首先可以肯定的是,fast是先走进环中的,两个指针如果相遇,也一定是在环中所相遇的

为什么slowfast一定会遇到呢:

  • 我们可以画个环,让fast在任意一个节点追slow。会发现最终都是这种情况:

image.png

slowfast再移动一步,就会遇到了,这是因为相对于slow来讲,fast每次移动都是在不断的缩短slow的距离,所以它们一定可以遇到

代码:(Java实现)

public boolean hasCycle(ListNode head) {
        
        ListNode slow = head;
        ListNode fast = head;

        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            
            //判断是否有环
            if (slow == fast) {
                return true;
            }
        }
        
        return false;
    }

复杂度分析

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

提交结果

image.png

进阶:环形链表 ||

力扣题目链接

进阶的这道题比上一道题多了一个返回值,我们不单单要判断链表中是否存在环,还需要返回开始入环的第一个节点

image.png

解题思路:(双指针)

我们通过上一道题知道了如何判断链表中是否存在环,这一道题要解决的就是如何返回如环的第一个节点

我们假设头节点到环入口的节点数为X环入口到相遇节点的节点数为Y,从相遇节点到环入口的节点数为Z image.png 从图中可以看出指针slow相遇节点位置的节点数X+Y,指针fast相遇节点位置的节点数X+Y+n(Y+Z),nfast在环内走的圈次数(Y+Z)为环内一圈的节点数

因为slow一次移动一个节点,fast一次移动两个节点。我们可以得到一个等式:

  • slow * 2 = fast => (X+Y) * 2 = X+Y+n(Y+Z) 两边各消除一个(X+Y),得 X+Y = n(Y+Z)

又因为我们要找环形的入口,所以我们需要取X,即X = n(Y+Z)-Z

再化简下,得 X = (n - 1)(Y+Z) + Z,这里的n一定大于等于1,因为fast至少要走一圈才能遇见slow

先拿n为1来举例,表明fast至少移动了1圈,才与slow碰到

n1时,X=Z

这就意味着:当两个指针相遇时,从头节点出发一个指针,从相遇节点出发一个指针,指针每次只移动一个节点,两个指针遇到后,它们相遇的位置就是环形的入口节点

也就是说的是,当链表里有环时,定义两个指针,index1从头节点出发,index2从相遇节点出发,两个指针每次只移动一个节点,当两个指针碰到时,返回他们所处的位置

代码:(Java实现)

public ListNode detectCycle(ListNode head) {
        ListNode A = head;
        ListNode B = head;

        while (B != null && B.next != null) {
            A = A.next;
            B = B.next.next;

            if (A == B) {
                ListNode index1 = head;
                ListNode index2 = B;

                while (index1 != index2) {
                    index1 = index1.next;
                    index2 = index2.next;
                }
                return index2;
            }
        }

        return null;
    }

复杂度分析

  • 时间复杂度:O(n) 第二次相遇中,慢指针须走步数 a < a + b;第一次相遇中,慢指针须走步数 a + b - x < a + b,其中 x 为双指针重合点与环入口距离;因此总体为线性复杂度;
  • 空间复杂度:O(1)

提交结果

image.png