15年腾讯的实习生招聘笔试题出现了这个经典问题:在只知道链表头指针的情况下,怎么判断链表中是否存在环。如果有环,判断环有多大,环的入口节点在哪里。
对这几个问题,整理了一下网友提供的思路,自己动手实现了一下。
首先,判断环的存在。用两个指针,分别以一次走一步和一次走两步的速度遍历单链表,若有环,两指针肯定会在环中相遇,若没有,快指针会先遍历到NULL跳出。
bool findc(node*head)
{ node*fast=head,*slow=head;
while(fast&&fast->next)
{fast=fast->next->next;
slow=slow->next;
if(fast==slow) break;
}
return !(fast==NULL || fast->next==NULL);
}
其次,判断环有多大。我们知道在环中,若快慢两指针从同一点出发,前进过程中第一次相遇时快指针肯定比慢指针多走一整圈。而快指针速度是慢指针的2倍,因此慢指针走的路程刚好是一圈:2vt=vt+C 即:vt=C
int i=0;
node*p=x; //x是环中某节点
node*q=x;
do{p=p->next->next;
q=q->next;
i++;
}while(p!=q);
cout<<"Size of circle is "<<i<<endl;
最后,判断环的起始点。如果,单链表头节点到环起始点距离a,快慢指针相遇点到环起始点距离x,在找环的过程中有S=a+x,2S=a+x+nC,则a+x=nC,即a=nC-x.
由此知道,若在头结点和相遇结点分别设一指针,同时出发,单步前进,则最后一定相遇在环入口相遇。
#include<iostream>
#include<malloc.h>
using namespace std;
typedef struct node{
int num;
node* next;
}node;
node*createlist(int n) //创建单链表
{ node*head=NULL,*t,*r=NULL;
for(int i=0;i<n;i++)
{ t=(node*)malloc(sizeof(node));
t->num=i+1;
t->next=NULL;
if(!head) {head=t;
r=t;}
else r->next=t;
r=t;
}
return head;
}
node* findcx(node*head) //返回快慢指针相遇节点的找环函数,bool findc(node*head)的变形
{ node*fast=head,*slow=head;
while(fast&&fast->next)
{fast=fast->next->next;
slow=slow->next;
if(fast==slow) break;
}
bool flag =!(fast==NULL || fast->next==NULL);
if(flag)
{ cout<<"A circle exist in the list"<<endl;
return fast;
}else{
cout<<"No circle!"<<endl;
return NULL;}
}
int main()
{ node*l1=createlist(7);
node*l2=createlist(8);
node*s=l1;
int i=7-1,j=8;
while(i--) s=s->next;
s->next=l2;
while(j--) s=s->next;
s->next=l2; //初始化带环的单链表
i=15;
node*t=l1;
while(t&&i--)
{ cout<<t->num<<" ";
t=t->next;
}
cout<<endl; //打印单链表
node*x=findcx(l1);
cout<<"Cross point: "<<x->num<<endl;
node*p=l1,*q=x;
while(p&&q)
{ p=p->next;
q=q->next;
if(p==q)break;
}
cout<<"Entrance of the circle: "<<q->num<<endl; //判断环入口
i=0;
p=q=x;
do{p=p->next->next;
q=q->next;
i++;
}while(p!=q);
cout<<"Size of circle is "<<i<<endl; //判断环size
return 1;
}