环形链表

82 阅读2分钟

题目描述

给你一个链表的头节点 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
解释: 链表中没有环

思路

双指针法:快指针每次前进2步,慢指针每次前进1步,如果快慢指针相遇,则说明有环,否则,无环。

image.png

当快慢快慢指针相遇时:

  1. 快指针移动的距离:x+y+k(y+z),k(y+z)表示[k圈];

  2. 慢指针移动的距离:x+y

` 这里有两个问题,分别是:

  1. 为什么快慢指针一定会相遇?
  2. 为什么快慢指针一定会在慢指针进入的第一圈相遇?

解释

为什么快慢指针一定会相遇?

快指针每次移动2位,慢指针每次移动一位,在进入环之后,快指针相对于慢指针是每次移动一位,所以快慢指针一定会相遇!

为什么快慢指针一定会在慢指针进入的第一圈相遇?

首先slow进环的时候,fast一定是先进环来了。 如果slow进环入口,fast也在环入口,那么把这个环展开成直线,就是如下图的样子:

image.png

可以看出如果slow 和 fast同时在环入口开始走,一定会在环入口3相遇,slow走了一圈,fast走了两圈。

重点来了,slow进环的时候,fast一定是在环的任意一个位置,如图:

image.png

那么fast指针走到环入口3的时候,已经走了k + n 个节点,slow相应的应该走了(k + n) / 2 个节点。

因为k是小于n的(图中可以看出),所以(k + n) / 2 一定小于n。

也就是说slow一定没有走到环入口3,而fast已经到环入口3了

说明了,在slow开始走的那一环已经和fast相遇了

代码

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */

/**
 * @param {ListNode} head
 * @return {boolean}
 */
var hasCycle = function(head) {
    let slow=head; //慢指针
    let quick=head; //快指针

    while(slow&&quick&&quick.next){
        slow=slow.next;
        quick=quick.next.next;
        if(slow==quick){
            return true;
        }
    }
    return false;
};

结果

image.png