给定一个单链表,只给出头指针h:
1、如何判断是否存在环?
2、如何知道环的长度?
3、如何找出环的连接点在哪里?
4、带环链表的长度是多少?
1、对于问题1,使用追赶的方法,设定两个指针ps、pf,从头指针开始,每次分别前进1步、2步。如存在环,则两者相遇;如不存在环,pf遇到NULL退出。
证明:如果存在环,则他俩一定在环中相遇。
设i是走的步数,n是环的长度,
从ps走进环里开始计算,此时pf应当有一个相距于ps的距离,设为Y。
假设走了i步他俩相遇,此时ps相对于环开始节点的距离是i步,pf是Y+2i步。
相遇时快指针多走了一圈 (Y+2i)-i= n => Y+i=n =>i=n-y
所以,追上的步数i取决于进入环时y的值。y是1的时候,是追的最费劲的,需要n-1步;y是n-1的时候最容易追,1步就追上了。
Boolean has_loop(Node *head) {
Node *pf = head; /* fast pointer */
Node *ps = head; /* slow pointer */
while(true) {
if(pf && pf->next)
pf = pf->next->next;
else
return FALSE;
ps = ps->next;
if(ps == pf)
return TRUE;
}
}
2、对于问题2,记录下问题1的碰撞点p,ps、pf从该点开始,再次碰撞所走过的操作数就是环的长度n。
证明:上面的公式i=n-y,当y=0时,就是第一次相遇时开始走,i=n。
/* step 2, how long is the loop */
int i = 0;
do {
ps = ps->next;
pf = pf->next->next;
i++;
} while(ps!=pf)
3. 假设单链表的总长度为L,头结点到环入口的距离为a,环入口到快慢指针相遇的结点距离为x,环的长度为n,慢指针总共走了s步,则快指针走了2s步。另外,快指针要追上慢指针的话快指针至少要在环里面转了一圈多(假设转了n圈加x的距离),得到以下关系:
逆时针方向走,pf还需要走的距离是y,
y+x=tn
=>y+nr-a=tn=>y=(t-r)n+a//y的距离就是a又加了几圈
由上式可知:若在头结点和相遇结点分别设一指针,同步(单步)前进,则最后一定相遇在环入口结点,搞掂!
int FindLoopPort(Node *head) {
Node *pf = head; /* fast pointer */
Node *ps = head; /* slow pointer */
while(true) {
if(pf && pf->next)
pf = pf->next->next;
else
return FALSE;
ps = ps->next;
if(ps == pf)
break;
}
pf = head;
int i = 0;
while(ps != pf)
{
ps = ps->next;
pf = pf->next;
i++;
}
return i;
}
4. 将2和3的长度相加就是链表的长度。