判断是否有环以及求环的入口节点。

发布于:2023-01-18 ⋅ 阅读:(823) ⋅ 点赞:(0)

目录

思路一、

环形链表求环入口节点

一、判断是否有环

1.当节点个数为偶数个,slow走到中间节点,如果fast为空,则没有环。

2.当节点个数为奇数个,slow走到中间节点,如果fast->next为空,则没有环。

 3.如果有环,那么slow和fast一定会相遇。

4.延伸:如果fast每次走三步,四步,n步是否能相遇。

1. 如果fast走三步,那么差距步为n = 2。

2.如果fast走四步,差距步n = 3。

二、计算环入口节点

   

1. 首先通过判断是否有环,找到相遇节点。

思路二、

将环形链表求入口节点转换为两个相交链表求交点

步骤一:求出环内slow与fast相遇节点,当相遇时,相遇节点为fast或者slow

步骤二:创建指针newhead,记录相遇节点的下一个节点,将相遇节点的next置为NULL

 步骤三:创建指针curh,curnh,变量counth,countnh,计算两个链表节点个数

步骤四:head和newhead同时走,它们一定会相交

三、

思路一代码实现

思路二代码实现


思路一、

环形链表求环入口节点

一、判断是否有环

        通过快慢指针来判断是否有环。

        慢指针为slow,每次走一步。快指针为fast,每次走两步。都从首节点head开始。

1.当节点个数为偶数个,slow走到中间节点,如果fast为空,则没有环。

2.当节点个数为奇数个,slow走到中间节点,如果fast->next为空,则没有环。

 

 3.如果有环,那么slow和fast一定会相遇。

        因为每当slow和fast向前走时,距离都会缩进1。slow处在环入口时,与正在环中的fast相距N。

        

        在移动的过程中,N = N-1、(N-1)-1.......1,0,直到N=0,slow和fast相遇。

4.延伸:如果fast每次走三步,四步,n步是否能相遇。

       

       可知,当slow处在环入口节点时,fast在环中,此时相距N。

1. 如果fast走三步,那么差距步为n = 2。

        如果N为偶数,那么N能被n除尽,与环的奇偶性无关N = N - 2、(N-2) - 2 .......2、0,能相遇。

        如果N为奇数,N则不能被n除尽,N = N - 2 、(N-2) - 2........1、-1,第一次fast追击上slow时,相距为1。

        N不能除尽,则判断是否相遇条件由N,变为fast在环内第一次追上slow之间的距离能否被除尽。

        假设环的节点数为C,fast在环内第一次追上slow,则slow与fast相距为C-1,此时判断是否相遇的条件为C-1是奇是偶,如果C-1仍然为奇数,则C-1不能被差距步n=2除尽,无法相遇。如果C-1为偶,则能被n除尽,能相遇。

2.如果fast走四步,差距步n = 3。

        可知,当slow处在环入口节点时,fast在环中,此时相距N。

        如果N能被n整除,那么能相遇。

        如果不能,设gap = N-n、(N-n)-n、......,如果(C-gap) 能被n整除,则能相遇,以此类推。
 

二、计算环入口节点

   

1. 首先通过判断是否有环,找到相遇节点。

     设头节点head到环入口节点距离为L,slow与fast相遇节点到环入口节点距离为X。环的节点数为C。

   

         可知,slow走过的距离为:L+X。

        (X的大小不会超过环的大小C,因为如果slow走了一圈,那么fast一定走了两圈多,肯定是在一圈以内与slow相遇了。)

        fast走过的距离:L+n*C+X。n为圈数,C为环的大小。

        (我们并不知道在slow进入环之前,fast在环内循环了多少次,如果L的距离很长,C的大小很小,那么可能fast已经循环了多次,则设n为fast循环的次数。)

        联合slow与fast:2(L+X) = L+n*C+X。(因为fast走的距离是slow的两倍)

        可得:L = n*C-X。

        所表达的含义为,创建一个指针meet从头节点开始出发。另一个指针fast(用fast和slow都一样)从相遇节点开始出发,meet和fast每次都走一步,它们会在入口节点相遇。

        L为指针meet从头节点head走到环入口节点的距离,与此同时,指针fast从相遇节点走了n*C的距离,还是在相遇节点,然后减去X,就是环入口节点,这两个节点会在环入口节点相遇。

        如果meet的地址等于fast的地址,则为环入口节点。

        

思路二、

将环形链表求入口节点转换为两个相交链表求交点

步骤一:求出环内slow与fast相遇节点,当相遇时,相遇节点为fast或者slow

步骤二:创建指针newhead,记录相遇节点的下一个节点,将相遇节点的next置为NULL

        不置空也行,则在计算两个链表的个数时,循环计数条件则变为 curh != fast, curnh != fast。

 步骤三:创建指针curh,curnh,变量counth,countnh,计算两个链表节点个数

       1.用curh记录head的地址,用curnh记录newhead的地址,防止在计算链表的节点个数,改变head和newhead的地址。

        2.使用abs求出counth与countnh的差值的绝对值,定义变量gap存放

        如果counth>countnh,让head先走gap步。

        如果counth<countnh,让newhead先走gap步。

步骤四:head和newhead同时走,它们一定会相交

        当head的地址等于newhead的地址,此时head或者newhead为链表的环入口节点。

三、

思路一代码实现

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

    //链表数为奇数则fast->next为NULL;
    //偶则fast为NULL;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        //相遇节点;
        if(slow == fast)
        {
            struct ListNode* meet = head;
            //meet从头节点出发,fast从相遇节点出发;
            //用fast或者slow从相遇节点出发;
            while(meet != fast)
            {
                meet = meet->next;
                fast = fast->next;
            }
            //相等跳出循环,返回fast或者meet;
            return meet;
        }
    }
    //没有环返回空;
    return NULL;
}

思路二代码实现

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

    //链表数为奇数则fast->next为NULL;
    //偶则fast为NULL;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        //相遇节点;
        if(slow == fast)
        {
            //创建新链表的头节点,为相遇节点的next;
            struct ListNode* newhead = fast->next;
            //记录两个链表的个数,防止修改head与newhead的地址;
            struct ListNode* curnh = newhead;
            struct ListNode* curh = head;

            //将相遇节点的next置为NULL;
            //不置空也行,则计数时的判断条件为curh != fast ,curnh != fast(fast为相遇节点);
            fast->next = NULL;

            int counth = 0;
            int countnh = 0;
            //计算链表个数
            while(curh)
            {
                curh = curh->next;
                counth++;
            }
            while(curnh)
            {
                curnh = curnh->next;
                countnh++;
            }
            //计算两个链表节点个数的差值的绝对值;
            int gap = abs(countnh-counth);

            //个数大的先走;
            if(counth>countnh)
            {
                while(gap--)
                {
                    head = head->next;
                }
            }
            else
            {
                while(gap--)
                {
                    newhead = newhead->next;
                }
            }

            //求两个链表的交点;
            while(head != newhead)
            {
                head = head->next;
                newhead = newhead->next;
            }
            //相等跳出循环,返回head或者newhead;
            return head;
        }
    }
    //没有环返回空;
    return NULL;
}

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到