这个东西其实挺naive的。
floyd判圈
大概就是快指针和慢指针以\(1 : 2\)的速度前进。
当快指针再次与慢指针相遇时,它们行进路程的差值一定为圈长的整数倍。
为什么?
考虑一般情况就是走了一条链后进了一个圈。
设链长为\(m\),圈长为\(n\),慢指针走了\(a\)圈,快指针走了\(b\)圈,它们相遇在下一圈的距离入圈点\(d\)的位置,则
\[
s = m + n * a + d \\
2s = m + n * b + d \\
\]
得\(dif = s = n * (a - b)\),有\(n | dif\)。
考虑模拟上述过程,在两个指针再次相遇时停下来。
此时将慢指针移动至初始结点,快指针不动,以\(1 : 1\)的速度继续前进。
由于\(m + d = n (b - 2a)\)即\(n | (m + d)\),思考一下,发现快慢指针一定能在入圈点相遇(此时慢指针走了\(m\),快指针距离入圈点\(d + m\),在模\(n\)意义下恰好为\(0\))。
模拟整个过程就可以通过\(\mathcal O(m + n)\)的时间得出答案。
int *h = head, *t = head;
do {
h = h->next;
if (h != null) {
h = h->next;
}
t = t->next;
} while (h != t || h != null);
if (h != null) {
t = head;
while (h != t) {
h = h->next;
t = t->next;
}
p = h;
}
brent判圈
开始令快慢指针指向起始节点,让慢指针保持不动,快指针走\(2 ^ i\)步。在快指针走每一步后,判断快慢指针是否相遇。如果走了\(2 ^ i\)步后没有相遇,则将慢指针的位置移到快指针处,并且\(i++\);否则找到了圈,退出即可。复杂度同floyd(常数更小)。
int *t = head, *h = head;
int s = 0, lim = 2;
while (1) {
if (h == null) {
// no loop
break;
}
++s;
if (h == t) {
// find loop
break;
}
if (s == lim) {
s = 0;
lim <<= 1;
t = h;
}
}