有环单链表

单链表有环的情况如上图所示,循环链表也属于有环的链表。

这里我们使用追逐法判断一个链表是否有环:设置两个指针slow和fast从头节点开始,slow每次移动一个节点,fast每次移动两个节点,如果fast遇到了NULL则表明链表没有环,停止循环。如果链表有环的话,那么这两个指针总会在某个位置相遇,相遇后停止循环。代码如下:

 1  //追逐法判断链表是否有环
 2     bool has_loop(){
 3         Node<Type> *fast=head,*slow=head;
 4         //fast一定是走在前面的指针,当它遇到NULL时,说明没有环
 5         while(fast&&fast->next){
 6             slow=slow->next;
 7             fast=fast->next->next;
 8             if(slow==fast) break;
 9         }
10         return !(fast==NULL||fast->next==NULL);
11     }

下面我们在以上的代码的基础上寻找环的起始节点。

当fast若与slow相遇时,我们假设fast已经在环内循环了n圈(n>=1)。假设slow走了s步,则fast走了2s步(fast步数还等于s 加上在环上多转的n圈),设环长为r,则:

2s = s + nr
s= nr

设整个链表长L,入口环与相遇点距离为x,起点到环入口点的距离为a。
a + x = nr
a + x = (n – 1)r +r 
a = (n-1)r + r-x

(r – x)表示相遇点继续前进到达到环入口点的距离。由此可知,从链表头到环入口点等于(n-1)循环内环+相遇点到环入口点的距离,于是我们在链表头、相遇点分别设一个指针,每次各走一步,两个指针第一次相遇点即为环入口点。

 1 //找到环的起始节点
 2     Node<Type>* find_loop_port(){
 3         Node<Type> *fast=head,*slow=head;
 4         while(fast&&fast->next){
 5             slow=slow->next;
 6             fast=fast->next->next;
 7             if(slow==fast) break;
 8         }
 9         if(fast==NULL||fast->next==NULL){
10             return NULL;
11         }
12         //slow从头开始和fast同步往前走,再次相遇时即为环的起始节点
13         slow=head;
14         while(slow!=fast){
15             slow=slow->next;
16             fast=fast->next;
17         }
18         return slow;
19     }

此外,我们也可以通过让slow在和fast第一次相遇后再走一圈来求环的长度。代码如下:

 1 //求环的长度
 2     int loop_length(){
 3         Node<Type> *fast=head,*slow=head;
 4         while(fast&&fast->next){
 5             slow=slow->next;
 6             fast=fast->next->next;
 7             if(slow==fast) break;
 8         }
 9         if(fast==NULL||fast->next==NULL){
10             return 0;
11         }
12         int count=0;
13         //当slow回到相遇点时,它走的长度即为环的长度
14         do{
15             slow=slow->next;
16             count++;
17         }while(slow!=fast);
18         return count;
19     }

既然我们已经知道了环的起始点和环的长度,如果要求整个链表的长度的话只需要将头指针到环起始点距离和环长度相加即可。

另外一道与此相关的题:写一个程序判断两个链表是否相交并找到交点。

这里提供两种思路:第一,将一条链表首位相连,然后从另一条链表的头指针开始往后走,如果发现有环则说明两个链表相交(当然先要确定链表原本是没有环的),环的起始点即为两个链表的交点。 第二,两条链表都先从头走到尾获取长度,知道长度差N后长的那个链表先走N步,然后双方一起走,走到第一个相同节点即为交点。

 

posted @ 2017-06-12 21:02  NoviScl  阅读(338)  评论(0编辑  收藏  举报