141. 环形链表

简单
 
相关标签
相关企业
 

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

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

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

 

示例 1:

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

示例 2:

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

示例 3:

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

 

提示:

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

 

进阶:你能用 O(1)(即,常量)内存解决此问题吗?

 

思路

快慢指针,俩指针从头节点出发,一个快指针每次走两步,一个慢指针每次走一步,如果链表存在环路,那么快指针会追上慢指针

 

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public boolean hasCycle(ListNode head) {
        if(head == null || head.next == null || head.next.next == null) return false;
        ListNode slow = head, fast = head;
        while(fast != null && fast.next!=null){
            slow = slow.next;
            fast = fast.next.next;
            if(slow == fast) return true;
        }
        return false;
    }
}

证明快慢指针算法(也被称为弗洛伊德的龟兔赛跑算法)的正确性,可以通过数学归纳法来完成。下面是证明的逻辑:

  1. 定义:假设链表由非环部分和环部分组成。设非环部分的长度为 𝑎a 个节点,环的长度为 𝑏b 个节点。

  2. 快指针进入环:当快指针(fast)进入环时,慢指针(slow)可能还在非环部分,或者已经在环内。

  3. 快慢指针相遇:当快指针在环内移动时,慢指针也在环内移动,但由于快指针的速度是慢指针的两倍,它们最终会在环内的某个点相遇。

  4. 证明相遇:假设在快指针进入环之前,快慢指针已经移动了 𝑘k 步(𝑘<𝑎k<a)。此时,快指针开始在环内移动,而慢指针继续在非环部分移动直到它也进入环。当慢指针进入环时,快指针已经在环内移动了 𝑘k 步。

  5. 环内相遇:在环内,快指针每走 𝑏b 步就会回到起点,而慢指针需要走 2𝑏2b 步。因此,每当快指针绕环一周,慢指针只绕了半周。这意味着,每过 𝑏b 步,快指针就会相对于慢指针多走 𝑏b 步。由于快指针和慢指针之间的初始距离是 𝑘k 步,经过 𝑘𝑏bk 轮之后,快指针将追上慢指针。

  6. 特殊情况:如果链表只有一个节点,或者没有节点,快慢指针会在第一步就相遇,这符合我们的预期。

  7. 没有环的情况:如果链表中没有环,快指针会在到达链表末尾时停止,而慢指针会在快指针之前停止,因此它们永远不会相遇。

 

posted @ 2024-06-02 10:32  Heinrich♣  阅读(8)  评论(0编辑  收藏  举报