使用两个slow, fast指针从头开始扫描链表。指针slow 每次走1步,指针fast每次走2步。如果存在环,则指针slow、fast会相遇;如果不存在环,指针fast遇到NULL退出。
就是所谓的追击相遇问题:
代码示例如下:
//判断链表是否有环,如果无环返回NULL ListNode* HasCircle(LinkList head){ if(head==NULL) return NULL; ListNode *slow=head; ListNode *fast=head; while(fast!=NULL){ slow=slow->next;//走一步 fast=fast->next; if(fast!=NULL) fast=fast->next; else return NULL; //如果slow==fast,则有环 if(slow==fast) return slow; }//while条件为假,则退出循环 return NULL; }
第一次碰撞点Pos到连接点Join的距离=头指针到连接点Join的距离,因此,分别从第一次碰撞点Pos、头指针head开始走,相遇的那个点就是连接点。
在环上相遇后,记录第一次相遇点为Pos,连接点为Join,假设头结点到连接点的长度为LenA,连接点到第一次相遇点的长度为x,环长为R。
第一次相遇时,slow走的长度 S = LenA + x;
第一次相遇时,fast走的长度 2S = LenA + n*R + x;
所以可以知道,LenA + x = n*R; LenA = n*R -x;
代码示例如下:
//判断一个链表是否有环,如果有,返回环的入口点;如果无环,则返回NULL ListNode* CircleEntrance(LinkList head){ if(head==NULL) return NULL; ListNode *slow=head; ListNode *fast=head; while(fast!=NULL){ slow=slow->next;//走一步 fast=fast->next; if(fast!=NULL) fast=fast->next; else return NULL; //如果slow==fast,则有环 if(slow==fast) break; }//while条件为假,则退出循环 if(fast==NULL)//无环退出 return NULL; else{ //求入口点位置 slow=head; while(slow!=fast){ slow=slow->next; fast=fast->next; } return slow; } }
已知环的起点,很容易求环的长度。
示例代码如下:
//如果一个链表有环,返回环的长度;无环,则返回0 int CircleLength(LinkList head){ if(head==NULL) return 0; ListNode *slow=head; ListNode *fast=head; while(fast!=NULL){ slow=slow->next;//走一步 fast=fast->next; if(fast!=NULL) fast=fast->next; else return 0; //如果slow==fast,则有环 if(slow==fast) break; }//while条件为假,则退出循环 if(fast==NULL)//无环退出 return 0; else{ slow=head; while(slow!=fast){ slow=slow->next; fast=fast->next; } //while()slow指向 int circlelen=1; ListNode *p=slow; //求环的长度 while(p->next!=slow){ ++circlelen; p=p->next; } return circlelen; } }
上述3中求出了环的长度;2中求出了连接点的位置,就可以求出头结点到连接点的长度。两者相加就是链表的长度。
typedef struct ListNode{ int data; struct ListNode* next; }ListNode,*LinkList; //尾插法创建带环的链表 LinkList CreateLinkList(){ LinkList head=NULL; ListNode *pre=NULL,*inter=NULL; //尾插法建立链表 for(int i=1;i<=6;i++){ //创建结点 ListNode *temp=(ListNode*)malloc(sizeof(ListNode)); temp->data=i; temp->next=NULL; if(head==NULL){ head=temp; pre=head; }else{ pre->next=temp; pre=temp;//尾插法 } if(i==6) inter=temp;//i==3值处为交叉点 } pre->next=inter;//建立链表的环 return head; }
int main(){ LinkList head=CreateLinkList(); int circlelen=CircleLength(head); if(!circlelen) cout<<"no circle"<<endl; else{ cout<<"has circle"<<endl; cout<<"circle entrance is "<<circlelen<<endl; } return 0; }
原文:http://blog.csdn.net/sxhlovehmm/article/details/44853699