如何判断一个单链表是否有环,若有环,找出环的入口?
下图是一个链表
设置两个链表指针fast, slow,初始值都指向链表头结点,然后两个指针都往后走,不同的是slow每次前进一步,即前进一个节点。fast每次前进两步,如果存在环,两个指针必定相遇。
因为只有存在环的情况,我们才可能出现走的快的指针能再次遇到慢的指针。
并且还有一点就是,若该链表存在环,则在慢指针还没走完一整个环的路程之前,两指针已经相遇。
为什么?因为从慢指针进入环入口开始计时,慢指针走完一圈的时间,此时快指针已经走了两圈。所以在慢指针走完一圈之前,两指针一定会相遇。
把两指针相遇点记为O。则慢指针已走的环路程记为x,环剩下的路程记为y
设slow在相遇前走了s步,则fast走了2s步,设环长为r,有2s=s+nr,即s=nr。
由上图可知a+x=s, x+y=r,而我们的目标是找到a的位置。a+x=s=nr=(n-1)r+r=(n-1)r+y+x,则a=(n-1)r+y. 这个公式告诉我们,从链表头和相遇点O分别设一个指针,每次各走一步,这两个指针必定相遇,且相遇的第一个点为环入口点。
struct Link
{
int data;
Link *next;
};
// 插入节点
void insertNode(Link *&head, int data)
{
Link *node = new Link;
node->data = data;
node->next = head;
head = node;
}
// 判断链表是否存在环
Link* hasCycle(Link* head)
{
Link *fast, *slow;
slow = fast = head;
while (fast && fast->next)
{
fast = fast->next->next;
slow = slow->next;
if (fast == slow)
return slow;
}
return NULL;
}
// 确定环的入口点,pos表示fast与slow相遇的位置
Link* findCycleEntry(Link* head, Link* pos)
{
while (head != pos)
{
head = head->next;
pos = pos->next;
}
return head;
}