两种判圈法——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;
	}
}
posted @ 2019-10-18 12:44  psimonw  阅读(733)  评论(0编辑  收藏  举报