判断单链表是否有环(转载)
问题:
1、如何判断一个单链表是否有环?
2、如果链表为存在环,如果找到环的入口点
问题1解法:
设置两个指针(fast, slow),初始值都指向头,slow每次前进一步,fast每次前进二步,如果链表存在环,则fast必定先进入环,而slow后进入环,两个指针必定相遇。(当然,fast先行头到尾部为NULL,则为无环链表)程序如下:
bool IsExitsLoop(slist *head) { slist *slow = head, *fast = head; while ( fast && fast->next ) { slow = slow->next; fast = fast->next->next; if ( slow == fast ) break; } return !(fast == NULL || fast->next == NULL); }
证明:
slow首次在A点进入环路时,fast一定在环中的B点某处。设此时slow距head长为x,B点距A点长度为y,环周长为s。因为fast和slow的步差为1,所以slow前行距离为y的时候,恰好会被fast在M点追上。因为y<s,所以slow尚未完成一次遍历。
问题2解法:
slist * FindLoopPort(slist * head) { slist * slow = head, * fast = head; while ( fast && fast -> next ) { slow = slow -> next; fast = fast -> next -> next; if ( slow == fast ) break ; } if (fast == NULL || fast -> next == NULL) return NULL; slow = head; while (slow != fast) { slow = slow -> next; fast = fast -> next; } return slow; }
证明:
当fast按照每次2步,slow每次一步的方式走,发现fast和slow重合,确定了单向链表有环路了
接下来,让fast回到链表的头部,重新走,每次步长不是走2了,而是走1,那么当fast和slow再次相遇的时候,就是环路的入口了。
这点可以证明的:
在fast和slow第一次相遇的时候,假定slow走了n步骤,环路的入口是在p步的时候经过的,那么有
slow走的路径: p+c = n; c为fast和slow相交点,距离环路入口的距离
fast走的路径: p+c+k*L = 2*n; L为环路的周长,k是整数
显然,如果从p+c点开始,slow再走n步骤的话,还可以回到p+c这个点
同时fast从头开始走的话,经过n步,也会达到p+c这点
显然在这个步骤当中fast只有前p步骤走的路径不同,所以当slow和fast再次重合的时候,必然是在链表的环路入口点上。
扩展问题:
判断两个单链表是否相交,如果相交,给出相交的第一个点(两个链表都不存在环)。
比较好的方法有两个:
一、将其中一个链表首尾相连,检测另外一个链表是否存在环,如果存在,则两个链表相交,而检测出来的依赖环入口即为相交的第一个点。
二、如果两个链表相交,那个两个链表从相交点到链表结束都是相同的节点,我们可以先遍历一个链表,直到尾部,再遍历另外一个链表,如果也可以走到同样的结尾点,则两个链表相交。