问题:
1.单链表是否有环
2.环的节点长度
3.环的入口节点
4.表头到环入口的节点长度
问题1.思路:
在表头设置fast、slow指针,fast一次遍历两个节点,slow一次遍历一个节点,若相遇则证明有环,反之则不存在环
代码:
/**
*引入快慢指针,若指针相遇则证明链表有环
*/
Node * findRing(Node *head)
{
Node * fast = head, *slow = head;
while (fast&&fast->next) {
fast = fast->next->next;
slow = slow->next;
if (slow == fast)
return slow;
}
return NULL;
}
问题2.思路:从fast、slow指针相遇开始,再次出发,再次相遇时slow走过的距离即为环的长度
代码:
/**
*meetPoint为快慢指针相遇的节点指针
*快慢节点再次相遇的时候,慢指针走过的长度即为环的长度
*/
int ringLen(Node * meetPoint) {
Node *fast = meetPoint;
Node *slow = meetPoint;
int ringLen = 0;
while (true) {
fast = fast->next->next;
slow = slow->next;
ringLen++;
if (fast == slow)
break;
}
return ringLen;
}
ps:可以直接让slow重相遇点出发,重新到达相遇点时,即为环的长度
问题3.思路
设指针p指向链表表头,q指向slow、和fast的相遇节点,两个指针同时开始遍历,当两个节点相遇时,相遇节点为环入口节点
代码:
Node * ringEntr(Node * head , Node * meetPoint) {
Node * p = head, *q = meetPoint;
while (p != q) {
p = p->next;
q = q->next;
}
return p;
}
https://blog.csdn.net/u011373710/article/details/54024366
上面这篇博客对问题三有比较详细的证明
这里再解释一下(思路都是由上面的博客得来的):
1.刚开始h、fast、slow在表头,环的入口为t,fast开始一步两个节点遍历链表,slow开始以一步一个节点遍历链表,h指针不动
2.当slow到达入口t时,设fast在m1,从t到m1的逆时针距离为b,环的长度为r,从m1到t的距离为r-b,同理从fast到slow的距离为也为r-b
3.这时fast可能已经绕了n圈,所以表头到t的距离a = nr +b
4.设m2为在slow、fast的相遇节点,slow和fast之间的速度差为1,距离差为r-b,时间(r-b)/1 = r-b,slow走过的路径为(r-b)*1=r-b,所以m2到t的距离为b,h这时从表头出发,slow继续遍历
5.h从表头出发走过距离为b时,slow到达t点,由3可得 a = nr + b 这时 a - b = nr h到t剩下的距离是环长度的整数倍,故当后到达t时必然与slow相遇
问题4.思路:
在问题3的基础上,从h出发时加上一个计数器,当h和slow相遇时既可以的到表头到环入口的距离
int getLenA(Node * head,Node * meetPoint) {
Node * p = head,*q = meetPoint;
int len = 0;
while (p != q) {
p = p->next;
q = q->next;
len++;
}
return len;
}