快慢指针
简介
快慢指针就是定义两根指针,移动的速度一快一慢,以此来制造出自己想要的差值。这个差值可以让我们找到链表上相应的节点。
- 求一个链表的中间值,我们将快指针每次走两步,慢指针每次走一步。当快指针到达末尾,慢指针就是中间值。
- 删除链表的倒数第 N 个值,我们将快指针每次走 1 步,慢指针每次走 1 步但是比快指针慢 N 步。当快指针到达末尾,慢指针就是需要删除的点。
- 判断链表是否是循环链表,我们将快指针每次走两步,慢指针每次走一步。当快指针和慢指针相遇就表示有环,否则没有。类似我们操场上跑步,如果是一个环,那么跑的快的人肯定会和跑的慢的人再次相遇。
示例
var hasCycle = function (head) {
let slow = head;
let fast = head;
while (fast) {
if (!fast.next) return false;
fast = fast.next.next;
slow = slow.next;
if (fast === slow) return true;
}
return false;
};
进阶
- 首先根据上一个方法先确定有没有环
- 其次我们再找到入环节点
D E
A B C F
H G
- 假设我们在 D 点相遇
- L 表示环的长度
- Lf 快指针走的路程
- Ls 慢指针走的路程
- S1 = ABC
- S2 = CD
- S3 = DEFGHC
- 相遇时候慢指针跑了 m 圈,Ls = S1 + m * L + S2
- 相遇时候快指针跑了 n 圈,Lf = s1 + n * L + S2
- 2 * Ls = Lf
- S1 + S2 = (n - 2 * m) * L
- 我们用 x 表示 (n - 2 * m), x >= 1
- S1 + S2 = x * L
- 我们用 y 表示 x - 1, y >= 0
- y * L + (L - S2) = S1
- S2 + S3 = L
- S1 = S3 + y * L
- 因此我们再使用一个慢指针,两个慢指针分别从 A D 两点出发,最后肯定会在 C 点相遇
- 假设走了 y * L 距离,从 A 点出发的指针还剩 S3 距离到达 C 点
- 而另一个指针肯定还在 D 点(转了 y 圈),这个时候他也剩 S3 距离到达 C 点
var detectCycle = function (head) {
let slow = head;
let fast = head;
let start = head;
while (fast) {
if (!fast.next) return null;
fast = fast.next.next;
slow = slow.next;
if (fast === slow) {
while (start != slow) {
slow = slow.next;
start = start.next;
}
return slow;
}
}
return null;
};