问答题 如何检测一个链表是否有环
【正确答案】
【答案解析】定义两个指针fast与slow,其中,fast是快指针,slow是慢指针,二者的初始值都指向链表头,slow每次前进一步,fast每次前进两步,两个指针同时向前移动,快指针每移动一次都要跟慢指针比较,直到当快指针等于慢指针为止,就证明这个链表是带环的单向链表,否则,证明这个链表是不带环的循环链表(fast先行到达尾部为NULL,则为无环链表)。具体实现代码如下:
public boolean IsLoop(Node head){
Node fast=head;
Node slow=head;
if(fast==null){
return false;
}
while(fast!=null&&fast.next!=null){
fast=fast.next.next;
slow=slow.next;
if(fast==slow){
return true;
}
}
return!(fast==null || fast.next==null);
}
上述方法只能用来判断链表是否有环,那么如何找到环的入口点呢?如果单链表有环,按照上述思路,当走得快的指针fast与走得慢的指针slow相遇时,slow指针肯定没有遍历完链表,而fast指针已经在环内循环了n圈(n>=1)。假设slow指针走了s步,则fast指针走了2s步(fast步数还等于s加上在环上多转的n圈),设环长为r,则满足如下关系表达式:
2s=a+nr
s=nr
设整个链表长L,入口环与相遇点距离为x,起点到环入口点的距离为a,则满足如下关系表达式:
a+x=Fir
a+x=(n-1)r+r=(n-1)r+L-a
a=(n-1)r+(L-a-x)
(L-a-x)为相遇点到环入口点的距离,从链表头到环入口点等于(n-1)循环内环+相遇点到环入口点,于是在链表头与相遇点分别设一个指针,每次各走一步,两个指针必定相遇,且相遇第一点为环入口点。
具体实现代码如下:
publieNode FindLoopPort(Node head){
Node slow=head, fast=head;
while(fast!=null&&fast.next!=null){
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;
}