1. 判断单链表是否有环
设置两个指针 fast 和 slow 遍历链表,初始时它们都指向链表的头指针,fast指针在遍历时每次移动两步,而slow指针在遍历时一次移动一步,这样,如果单链表中存在环,fast指针必定先进入环,待slow指针也进入环时,fast指针和slow指针所指结点之间的距离会在遍历的过程中一步一步的缩小直到它们都指向同一个结点;如果链表中不存在环,那么fast指针会先到达链表的尾部,算法如下:
int isExitLoop( LinkList list ){
LinkList fast = list, slow = list;
while( fast != NULL && fast->link != NULL ){
slow = slow->link;
fast = fast->link->link;
if( fast == slow )
return 1;
}
return 0;
}
注意上面的算法是用C语言描述,在C语言中没有bool类型,于是使用int 类型0和1来表示是否存在环,如果存在环则返回1,否则返回0;如果采用C++描述,则函数返回值类型可定义成bool类型,返回值语句可以写成:
return ! (fast == NULL || fast->next == NULL);
2. 如果存在环,找到环的起始结点
当fast指针等于slow指针时,slow指针肯定还没有遍历完整个链表,而此时fast指针已经在环内循环了n圈(n>=1),假定从链表头指针开始slow走了s步,则fast走了2s步,fast所走的步数还等于s加上fast指针比slow指针在环内多走的n圈。设环长为r,则:
2s = s + nr;
=>s = nr;
设整个链表长度为L,环的入口结点到相遇结点的距离为x, 起点到环的入口结点的距离为a.
a + x = nr;
=> a + x = (n-1)r + L - a;
=> a = (n-1)r + (L - a - x);
=> 由链表的头结点到环入口结点的距离等于n-1圈环的长度+相遇点到环入口结点的距离,于是,当我们在链表头部和相遇处分别设一指针,每次各走一步,则两指针必定相遇,且相遇的第一个结点即为环的入口结点,算法描述如下:
LinkList findLoopPort( LinkList list ){
LinkList fast = list, slow = list;
while( fast != NULL && fast->link != NULL ){
slow = slow->link;
fast = fast->link->link;
if( fast == slow )
break;
}
if( fast == NULL || fast->link == NULL )
return NULL;
slow = list;
while( fast != slow ){
slow = slow->link;
fast = fast->link;
}
return fast;
}
注意,上述的距离是指按照指针遍历链表的方向向前到达目标结点的距离。
"当fast指针等于slow指针时,slow指针肯定还没有遍历完整个链表", 这个可以自己画一个表盘结构的示意图证明。