一、 有环链表
什么是有环链表,只需要一张图就能说明什么是有环链表,如下:
二、如何实现有环链表
这篇文章,有详细的实现。如何创建一个有环链表
三、判断是否存在环
3.1 先解决第一个问题,如何判断链表中存在环???
采用一个快慢指针fast
、slow
,fast
每次移动两位,slow
每次移动一位。如果最后他们可以相遇,说明存在环。
3.2 两颗指针一定会相遇吗?
会, 因为每一次移动他们之间的差距只会增加 1 位,快慢指针进入环以后,每一次快指针都会比慢指针多移动一步,因此快指针必定会追上慢指针。
3.3 实现
根据上面的思路完成代码:
ini
function resolve(head) {
// 定义快慢指针
let fast = head;
let slow = head;
// 不用判断 p ,因为p 要走的路,q 都走过了,q.一定不为null
while (fast !== null && fast.next !== null) {
slow = slow.next; // `slow` 每次移动一位
fast = fast.next.next; //`fast`每次移动两位
if (fast === slow) {
return true; // 相遇说明存在环
}
}
}
console.log(resolve(list.head));
四、找到环的入口
4.1 如何找到环的入口呢? 先分析一波
- slow 走的距离:
a + b
- fast 走的距离
a + n(b+c) + b
4.2 slow一定是 a+b
吗?slow一定不可能在相遇之前走完一个环吗?
是的,slow 不可能走完一个环。我们假设slow 能走完一个环,那么在这段时间内fast 就必定能走完两个环,在slow 走完之前,他们一定已经相遇了。(其实可以,但是需要从同一个位置出发,也就是相遇点和出发点都在环的入口处,此时slow刚好有机会走完一个环,但是在这之前已经相遇了。没有讨论的意义)
4.3 推导
根据以上信息,可以得到一个等式: 2(a + b) = a + n(b + c) + b
化简等式:
a= n(b+c) - b
= n(b+c) - b - c + c
=n(b+c) - (b+c) + c
= (n-1)(b+c) + c
也就是说:a = (n-1)(b+c) + c
这意味着:
- b+c 不就是环的长度吗,而 c 就是相遇点到环入口的距离。
- 如果我们现在开始定义两颗指针,p1从头指针开始 ,p2 从相遇点开始,每次两个指针都移动一位。
- 经过(n-1)(b+c) + c 次移动后,p1 和 p2 一定会在口处相遇。
- 只需要定义一个计数器,记录相遇之前移动了多少次,就是 a 的大小。
- 相遇的之前,p2 移动了n 圈,外加一段 c.
4.4 实现:
js
function resolve(head) {
// 定义快慢指针
let fast = head;
let slow = head;
// 不用判断 p ,因为p 要走的路,q 都走过了,q.一定不为null
while (fast !== null && fast.next !== null) {
slow = slow.next; // 慢指针每次移动一位
fast = fast.next.next; // 快指针每次移动两位
if (fast === slow) {
let p1 = head;
let p2 = fast;
let count = 0; // 记录移动的次数
while (p1 !== p2) {
p1 = p1.next; // p1,p2每次后移动一位
p2 = p2.next;
count += 1;
if (p1 === p2) return count; // 相遇结束循环
}
}
}
return -1; // 代表没有环
}
console.log(resolve(list.head));
完美,撒花~~~~~