两种判圈法——floyd和brent

这个东西其实挺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;
	}
}
原文地址:https://www.cnblogs.com/psimonw/p/11697526.html