两个环形链表问题
①判断链表中是否存在环。
②求环形链表的入口节点。
1 判断链表中是否存在环
对于一个存在环的链表,在遍历链表时,指针进入到环状链表后一定是一个死循环,根据这个特性,我们可以设置一个快指针和一个慢指针,因为是一个循环,所以快慢指针终将会相遇,相遇就代表这个链表存在环。
快指针一次走两步,慢指针一次走一步,如果有环,快指针和慢指针终将相遇。
1.1 问题:
为什么快指针走两步,慢指针走一步就一定行呢,快指针一次走三步呢,走四步呢,走N步呢,是否也行得通呢??
对于这个问题,我们知道如果快指针比慢指针多走一步。
假设图中一段表示一步,当slow和fast都进入圆环时候。
此时slow和fast相差的距离是2。如果再走一步。
此时fast和slow距离为1,所以我们可以理解到,当slow和fast在环中,如果slow和fast的位置不同,当slow每走一步,fast走两步,那么fast和slow之间的距离每次就会少一步,以此往复,fast和slow终将相遇。
如果fast每次走三步甚至n步呢?
根据以上我们可以知道,当slow和fast进入环后,fast每次走三步,slow每次走一步,那么fast和slow的距离就会每次少两步,那么如果再进行下一步之前fast恰好出现在slow的前一个,那么下一步过后,fast就会把slow略过,进而可能导致死循环,永远也无法找到。
1.2 完整代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
bool hasCycle(struct ListNode *head) {
if(head==NULL||head->next==NULL)
return false;
struct ListNode*slow=head;
struct ListNode*fast=head->next;
while(fast&&fast->next)
{
if(slow==fast)
{
return true;
}
slow=slow->next;
fast=fast->next->next;
}
return false;
}
2 求环形链表的入口节点。
这个问题是让我们找到从头结点开始进入到环链表的第一个节点。也是就是图上的2的结点。
2.1 方法一
定义慢指针和快指针,如果存在环链表,则分割快指针和慢指针的相交位置。
此时将问题转换成了两条单链表相交求交点的问题。(后续更新)。
2.2 方法二
假设头节点到环入口节点的距离为L,入口节点到相遇结点距离为X,假设环周长为C。
如果我们算slow和fast到相遇走过的步数的话,我们可以得到:
所以如果要找环入口头结点,我们只需要用一个指针phead从头结点开始遍历,一个指针从meet的地方继续向下走,一步一步的,由于meet在环内,所以meet走(N-1)C的距离之后还在原处,所以当meet等于phead的时候,这个位置就是环入口节点。
完整代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode *detectCycle(struct ListNode *head) {
if(head==NULL||head->next==NULL)
return NULL;
struct ListNode*slow=head;
struct ListNode*fast=head;
struct ListNode*meet=NULL;
while(fast&&fast->next)
{
slow=slow->next;
fast=fast->next->next;
if(slow==fast)
{
meet=slow;
break;
}
}
if(meet==NULL)
{
return NULL;
}
slow=head;
while(slow)
{
if(slow==meet)
{
return meet;
}
slow=slow->next;
meet=meet->next;
}
return NULL;
}