目录
- 一、判断一个链表是否有环
 - 二、找到链表入环的第一个节点
 
一、判断一个链表是否有环
给你一个链表的头节点 head ,判断链表中是否有环。
 如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。
 如果链表中存在环 ,则返回 true 。 否则,返回 false 。
 
 
 
 解题思路:
用快慢指针,慢指针一次往后走一步,快指针一次往后走两步。这样快指针会比慢指针先入环,当慢指针入环被快指针追上时,说明这是一个环形链表,当快指针为空或者指向空时,说明这不是环形链表,返回NULL。

 为什么肯定slow 和 fast一定会相遇呢?我们假设当slow入环时,fast到slow的距离为N。
 
 而slow 每次往前走一步,fast 每次往前走两步,那么它们的距离是逐渐递减的。
 
 最后它们之间的距离为 0 时,它们就相遇了。
那问题来了,如果fast 一次走三步,四步,五步,它能不能在环中与slow相遇呢? 我们假设fast一次走三步。
 fast到slow的距离继续为N
 
slow 一次走一步, fast一次走三步 , 那么N是每次少2
 
 此时会出现两者情况,一种情况是相遇了,还有一种情况是没相遇。
 当没相遇的时候,fast已经跑到了 slow的前面,也就是它们此时的距离是-1。
 
 设这个环的长度为C,所以它们的距离 N 此时是等于 C-1的。而因为fast一次是走三步的,所以N每次会-2。 当N等于0时它们才会相遇,这也就意味,C-1 如果是偶数,那么它们才会相遇,如果C-1 是奇数,那它们永远不会相遇。所以,fast 一次走两步,它一定会和slow在环里面相遇,slow不用走完一圈。如果fast一次走3,4,5…n步,那么是有可能永远不会与slow相遇的。
代码如下:
bool hasCycle(struct ListNode *head) {
    struct ListNode* slow = head;
    struct ListNode* fast = head;
    //如果fast为空或者指向空,说明不是环形链表
    while(fast && fast->next)
    {
        //慢指针一次走一步,快指针走两步
        fast = fast->next->next;
        slow = slow->next;
        //如果fast 与 slow 相遇,说明是环形链表
        if(fast == slow)
        {
            return true;
        }
    }
    //出了循环,说明不是环形链表
    return false;
}
 
题目链接
二、找到链表入环的第一个节点

 
 
判断了一个链表是否有环之后,那么如何找到链表入环的第一个节点呢?我们设链表头节点到入环的第一个节点为L。
 
 我们再设 入环的第一个节点,到slow 与fast相遇的节点的距离为X。
 
 那么slow 走的距离是 L + X
 设圆的长度为C
 那么fast走的距离是 L + NC + X
 N代表走的圈数,因为当环长度很小,而L长度非常大的时候,slow 入环时,fast 不可能只走一圈,一圈是极端情况,这样我们就可以推导出。
 2(L + X ) = L + NC +X
 L + X = N * C
 L = N * C - X
 L = (N-1) *C + C - X
 而N - 1 是 fast走的圈数,我们可以省掉
 所以 L = C - X
 
 代码:
struct ListNode *detectCycle(struct ListNode *head) {
    struct ListNode* slow , *fast;
    slow = fast = head;
    while(fast && fast->next)
    {
       
        fast = fast->next->next;
        slow = slow->next;
         if(fast == slow)
        {
            //保存相遇节点
            struct ListNode* MeetNode = slow;
            struct ListNode* NewHead = head;
            //相遇点和头节点同时走
            while(MeetNode != NewHead)
            {
                MeetNode =  MeetNode->next;
                NewHead =   NewHead->next;
            }
            return MeetNode;
        }
    }
    return NULL;
}
 
当然,这题还有第二种方法,不过容易理解,实现起来可能有点困难。
 我们可以把相遇节点的下一个节点,作为一个新链表的头节点,而上一个节点作为两个链表的尾节点。而这俩个相交的节点,就是入环的第一个节点
 
 至于如何找到两个链表的交点,上一篇博客第八题有说明,传送地址
所以第二种方法,我们只需要找到它们的newhead和head的相交节点就可。
代码
struct ListNode *detectCycle(struct ListNode *head) {
        struct ListNode* slow , *fast;
        slow = fast = head;
        while(fast && fast->next)
        {
            slow = slow->next;
            fast = fast->next->next;
            //如果俩节点相遇
            if(slow == fast)
            {
                //新节点为相遇的下一个节点
                struct ListNode* NewHead = slow->next;
                struct ListNode* OldHead = head;
                //求出新节点 和 原头节点 到 尾节点(slow)的长度 
                int lenNew = 1;
                int lenOld = 1;
                while(NewHead != slow)
                {
                    NewHead = NewHead->next;
                    lenNew++;
                }
                while(OldHead!= slow)
                {
                    OldHead = OldHead->next;
                    lenOld++;
                }
                //相差多少个长度
                int gap = abs(lenOld - lenNew);
                //找到较长和较短的链表
                struct ListNode* LongList = head;
                struct ListNode* ShortList = slow->next;
                if(lenOld < lenNew)
                {
                    LongList = slow->next;
                    ShortList = head;
                }
                //让长节点先走gap次
                while(gap--)
                {
                    LongList = LongList->next;
                }
                //然后俩个节点同时走
                while(LongList != ShortList)
                {
                    LongList = LongList->next;
                    ShortList = ShortList->next;
                }
                //此时就是相交节点
                return ShortList;
            }
        }
    return NULL;
}
 
题目链接



![[操作系统笔记]基本分页存储管理](https://img-blog.csdnimg.cn/9f5288d6d9c5488891d8b5730ccbecca.png)
![[附源码]计算机毕业设计springboot物业管理系统](https://img-blog.csdnimg.cn/2ed124b3476242e2b26a08d95d027d13.png)





![洛谷千题详解 | P1019 [NOIP2000 提高组] 单词接龙【C++、Java语言】](https://img-blog.csdnimg.cn/74ba48b1a27f40729548fb1892d28383.png)








