这个东西其实挺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: len = s
break;
}
if (s == lim) {
s = 0;
lim <<= 1;
t = h;
}
}