今天Mayuyu来带领你们讨论如下三个问题:
(1)如何判断一个单链表是否存在环 ?
(2)如果存在环,如何找到环的入口点?
(3)两个链表中有环时,如何判断相交?
问题一
我们设置两个指针,分别是fast和slow,初始都指向这个单向链表的表头,fast每次走两步,而slow每次走一步,
所以,我们知道如果有环,那么fast先进入环,slow后进入环,在经过若干次这样的步骤,必定会有fast与slow
相遇,那么我们就能判断是否存在环了。如果不存在环,则fast先走到链表尾。
所以我们很容易得到如下代码:
bool isLoop(list *head)
{
list *slow = head;
list *fast = head;
while(fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
if(slow == fast) return true;
}
return false;
}
问题二
上面问题一我们已经能判断一个单向的链表是否存在环,如果一个单向链表存在环,我们如何求它的环入口点?
如下图
首先,我们设head到入口点的距离为,入口点到相遇点的距离为
,沿着相遇点继续走到入口点的距离为
,
链表总长度为,fast指针与slow指针相遇时slow一共走了
步,那么很明显fast就走了
步,相遇时fast比
slow多走了圈。
所以我们很容易得到,即
又由于,所以
,得到:
这个表达式说明从相遇点到入口点的距离与head到入口点的距离取余后是相等的。所以我们可以在相遇点和head处
各设置一个指针,然后同时走,它们会在入口处相遇,这样我们也就能找到这个入口了。
代码如下:
list *FindPort(list *head)
{
list *slow = head;
list *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;
}
问题三
实际上,我们先要明确一个事实,那就是如果一个单链表有环,另一个单链表没有环,那么这两个单链表一定不会
相交。这是单链表的next指向的唯一性决定的,一个节点不可能有两个或者多个后继分支。
所以,如果是两个有环链表相交,那么它们一定存在一个公共的环,大致如下两个图形:
所以,明确了以上事实之后,我们可以这样来做:
先分别求出这两个有环链表的环的入口点,如果入口点相等,那么说明这两个有环链表就相交了。如果不相等,就有
两种情况:
(1)两个有环链表不相交
(2)它们相交但环入口点不同
那么我们如何判断,这个就比较简单了。
我们设一个环的入口点为port,在另一个环的入口点设置一个指针p往后走,如果一周内出现p->next == port,
那么说明这两个有环链表共环,也就是说相交,否则这两个有环链表不相交。