【数据结构初阶】环形链表

目录

  • 一、判断链表中是否存在环
    • 思路
    • 分析
  • 二、求环的长度
  • 三、求入环点

一、判断链表中是否存在环

题目链接:
判断链表中是否存在环

思路

把它转换为追击问题(快慢指针)
快指针比慢指针多走一步,当快指针正好进入环时,慢指针刚好走到一半;
当慢指针正好入环时,快指针在环里的位置是不确定的,可能已经绕环好几圈了,也可能一圈也没绕完,这个取决于环的长度。
快慢指针都进环后,剩下的问题就是追击问题了,当快指针追上慢指针,说明链表有环。
【数据结构初阶】环形链表_第1张图片

代码思路:
1.快指针比慢指针多走一步;
2.情况一:如果fast走到空,说明链表是没环的; 情况二:进环-》追击-》追上-》证明链表有环;

bool hasCycle(struct ListNode *head) 
{
    struct ListNode *slow=head;
    struct ListNode *fast=head;

    while(fast&&fast->next)
    {
        fast=fast->next->next;
        slow=slow->next;
        if(fast==slow)
        {
            return true;
        }
    }

    return false;
}

【数据结构初阶】环形链表_第2张图片

分析

问题1:为什么slow走一步,fast走两步,他们会相遇?会不会错过?请证明:

【数据结构初阶】环形链表_第3张图片

假设slow进环时,fast和slow的距离为N;
slow每走1步,fast走2步
他们之间的距离每次缩小1:即N,N-1,N-2…3,2,1,0,当距离为0时就追上了;

问题2:如果slow走1步,fast走3步,他们会不会相遇?请证明:

假设slow进环时,fast和slow的距离为N;
slow每走1步,fast走3步,他们之间的距离每次缩小2:
情况一:(当N为偶数)N,N-2,N-4…4,2,0—能追上
情况二:(当N为奇数)N,N-2,N-4…5,3,1,-1—错过,设环的周长为C,此时slow和fast的距离变为C-1,
【数据结构初阶】环形链表_第4张图片

进入新的一轮追击:
情况一:(当C为偶数,则C-1为奇数)C-1,C-3,C-5…5,3,1,-1—错过,此时fast和slow不可能再相遇了
情况二:(当C为奇数,则C-1为偶数)C-1,C-3,C-5…4,2,0—能追上

总结:
如果slow走1步,fast走X(X>2)步,他们会不会相遇?请证明: 主要看fast和slow他们之间的每走一步相差的距离,如果相差的距离为1,则一定能追上;如果相差的距离为大于1,要分情况讨论,看他们之间的距离是奇数还是偶数;

二、求环的长度

思路:让slow和fast相遇->slow再走一圈(同时记个数)

三、求入环点

题目链接:
求入环点

方法一:(利用fast走的路程永远是slow的两倍建立等式
假设:
起始点~入口点距离为L;
入口点~相遇点距离为X(0<=X 环的周长:C;

【数据结构初阶】环形链表_第5张图片
slow走过的距离为L+X;
fast走过的距离为L+nC+X;

成立等式:fast走过的距离是slow的两倍=》L+nC+X=2(L+X)=》整理得:L=nC-X
结论:一个指针从起始点走,一个指针从入口点走,会在入口点相遇

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *detectCycle(struct ListNode *head) 
{
    struct ListNode *fast=head;
    struct ListNode *slow=head;
    struct ListNode *meet=NULL;
    struct ListNode *rhead=head;
    
    //找相遇点
    while(fast&&fast->next)
    {
        fast=fast->next->next;
        slow=slow->next;
        if(fast==slow)
        {
            //一个指针从起始点走,一个指针从相遇点走
            meet=fast;
            while(meet!=rhead)
            {
                meet=meet->next;
                rhead=rhead->next;
            }
            return meet;

        }
    }

    return NULL;
}

方法二:(找入口点,转换成找链表交点(链表相交问题)

相遇点跟相遇点的下一个节点直接断开;
一个指针从起始点开始走算长度,一个指针从相遇点的下一个节点开始走算长度,然后再让长的先走,相交的那个点就是入口点

【数据结构初阶】环形链表_第6张图片

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */

int Compare(struct ListNode *circle,struct ListNode *rhead)
{
    int d1=0;
    int d2=0;

    while(circle)
    {
        circle=circle->next;
        d1++;
    }

    while(rhead)
    {
        rhead=rhead->next;
        d2++;
    }

    return d1-d2;

}



struct ListNode *detectCycle(struct ListNode *head) 
{
    struct ListNode *fast=head;
    struct ListNode *slow=head;
    struct ListNode *meet=NULL;

    
    //找相遇点
    while(fast&&fast->next)
    {
        fast=fast->next->next;
        slow=slow->next;
        if(fast==slow)
        {
            //断开结点
            slow=slow->next;
            fast->next=NULL;

            //判断哪一段长
            struct ListNode *rhead=head;
            struct ListNode *circle=slow;
            int ret=Compare(circle,rhead);

            fast=circle;
            slow=rhead;
            if(ret<0)
            {
                fast=rhead;
                slow=circle;
            }
            
            //长的先走
            int distance=abs(ret);
            while(distance)
            {
                fast=fast->next;
                distance--;
            }

            //一起走
            while(fast!=slow)
            {
                fast=fast->next;
                slow=slow->next;
            }

            return fast;

        }
    }

    return NULL;
}

【数据结构初阶】环形链表_第7张图片

你可能感兴趣的:(数据结构初阶,链表,数据结构,算法)