环形链表判断、入环问题

给你一个单链表如何去判断有环呢?如何求环形链表第一次入环的节点呢?

在还未接触这种题之前是一点头绪都没有,十分佩服能解决的人。

问题是 :给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

对于这个问题第一步肯定先是判断该链表是否有环,想要判断该链表是否有环那就需要用到快慢指针,什么是快慢指针呢?就是一个指针走一步,另一个指针走两步,如果两个指针会相等就说明该链表有环,反之没有环,返回NULL。

代码示例:

cs 复制代码
bool hasCycle(struct ListNode *head) {
    struct ListNode* slow = head,*fast = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if(slow == fast)
        return true;
    }
    return false;
}

看到这里有些人就会问,为什么一个指针走一步,另一个走两步,而不是一个走一步,另一个走三步甚至更多呢?

首先两个指针相遇肯定是在环中,在什么地方相遇我们是不知道的,当一个指针走一步,另一个走两步,那么当两个指针都在环中的时候,两个指针的距离就在一步一步减小,直到距离等于零两个指针就相等了。

如果一个指针走一步,另一个指针走三步甚至更多,就拿走三步来举例吧,那么当两个指针都在环中,每循环一次,两个指针的距离就缩减两个节点,当两个指针之间相差节点数为偶数时,两个指针可以相遇,但是两个指针之间相差节点数为奇数时,两个指针永远不可能相遇。

所以两个指针走的步数之差为1,两个指针就可以在环中相遇,步数只差不为1可能相遇,也可以永远不会相遇

判断完是否有环后,接下来就是解决入环的第一个节点问题,解决这个问题有很多种解法,这里只介绍两种:

第一种:创建两个指针,一个指向头结点,一个指向相遇的节点,两个指针一起走,当两个指针相遇的时候就是入环的第一个节点。

cs 复制代码
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* meet = slow;
            struct ListNode* cur = head;
            while(1)
            {
                if(meet == cur)
                  return meet;
                 meet = meet->next;
                cur = cur->next;
            }
        }
    }
    return NULL;
}

那么为什么从一个指针从头结点开始走,一个指针从相遇节点开始走,他们相遇的节点就是入环的第一个节点呢?

证明:

我们设头结点和入环点之间的距离为L,入环点和相遇点距离为X,环的周长为C。

首先指针slow做一步,指针fast走两步,所以fast走的距离是slow的两倍,出图中可以看出,两个指针相遇之前fast至少要在环中走一圈,fast的步数可以表示为L+X+n*C,slow的步数为L+X。

所以L+X+n*C = 2L+2X

L = n*C + X 这样可能还不太好看出来,可以变一下形:L = (n-1)*C + C-X

从以上可以得出一个结论,一个指针从起始点开始走,一个指针从相遇点开始走,他们会在入环点相遇。

第二种方法:

首先得到相遇的那个节点,然后把相遇点切开,记录下相遇点的下一个节点,这就变成了求链表相交点。

思路:一个指针指向头结点,另一个指向相遇节点的下一个节点,计算两个链表的长度,得出两个链表的长度差,让长的那个链表先走差距步,然后两个指针一起走,相等的节点就是入环点

这个相比第一种方法更好理解,但是代码相对麻烦,第一种理解比较难,代码简单。

相关推荐
刘马想放假4 天前
Modbus 全栈技术解析:TCP、RTU、ASCII、RTU over TCP
数据结构·网络协议
北域码匠5 天前
冒泡排序太慢?鸡尾酒排序双向优化,原生 C# 零第三方库完整代码
数据结构·排序算法·泛型·c# 算法·鸡尾酒排序·原生 c# 开发·冒泡排序优化·嵌入式算法
Darling噜啦啦12 天前
列表转树算法深度解析:从 Map 到 Reduce 的两种实现,面试高频考点
数据结构·算法·面试
小小工匠13 天前
Redis - 事务机制:能实现 ACID 属性吗
数据结构·redis·性能优化·并发·持久化
玖玥拾13 天前
C/C++ 数据结构(七)栈、容器适配器
c语言·数据结构·c++··容器适配器
Qres82113 天前
算法复键——树状数组
数据结构·算法
牛油果子哥q13 天前
并查集(DSU)超精讲,路径压缩、按秩合并、万能模板、连通性判定、最小生成树与刷题实战全解
数据结构·c++·最小生成树·并查集
凌波粒13 天前
LeetCode--491.递增子序列(回溯算法)
数据结构·算法·leetcode
疯狂成瘾者13 天前
Java 集合 LinkedList 详解:链表结构、常用方法和队列使用
java·开发语言·链表
WL学习笔记13 天前
单项不带头不循环链表
数据结构·链表