ListOJ14:环形链表II(寻找环的入口点)

目录


题目描述

原题:142. 环形链表 II

前面我们探究了如何判断环形链表:
ListOJ13:环形链表(判断是否为环形链表)

那我们如何找到这个环的入口点呢?我们先来看看这题OJ:

思路分析

思路一

第一种思路简单:

由上篇:
ListOJ13:环形链表(判断是否为环形链表)

我们得知,环形链表中的快慢指针一定会相遇的:

这时候我们将环从相遇点断开:

那问题转化为求相交链表的相交点了:

这个问题我们在这篇博客已经详细讨论过了:
160. 相交链表

这里不多加赘述:代码在下面展示;

思路二

这个思路写起来很简单,但是理解起来相对难;

我们先来看代码:

c 复制代码
typedef struct ListNode ListNode;
struct ListNode* detectCycle(struct ListNode* head) {
    ListNode* fast, * slow;
    fast = slow = head;
    while (fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if (slow == fast)
            break;
    }
    if (fast == NULL || fast->next == NULL)
    {
        return NULL;
    }
    ListNode* meet = fast;
    while (head != meet)
    {
        head = head->next;
        meet = meet->next;
    }
    return meet;
}

这个代码不难理解:

1、用指针meet指向链表的快慢指针相遇的节点;

2、同时移动meet指针和头指针;

3、当两个指针指向同一个节点时,该节点就是环的入口点。

证明

(设环的长度为C)当慢指针slow到达相遇点时,slow走过的距离设为L+X ,则快指针fast走过的距离为L+X+N*C (N是slow为到达环入口点时,fast所走过环的圈数, N大于等于1)

为什么slow走过的距离是 L+X, 而不是L+X+圈数?

我们来看看slow走最大的路程的情况:

此时,slow走一圈fast走两圈追上slow:

由于走过前面 L长度的节点后,fast和slow之间一定有距离,所以当slow到达环的入口点时,fast可能不在环的入口点,所以slow肯定不会走一圈的

fast走过的距离为什么是 L+X+N*C,为什么不是L+X+N,并且N大于等于1?

这里我解释一下 "N*C,并且N大于等于1",由于slow一次移动一个节点,fast一次移动两个节点,相同的时间下,fast的速度大于slow的速度,两者走过的距离肯定不相同,所以在fast走第一圈内,slow和fast不会相遇,只有当fast超过slow一圈或几圈后,两者才能相遇。

或者我们这样理解,但L远远大于C时:

此时fast就不止走一圈了;

又因为fast走过的距离是slow走过距离的二倍,所以有:

L+X+NC = 2(L+X)

即 L = N*C - X

也就是说相遇点meet和head同时向后走,一定会在环的入口处相遇:

由此我们得出结论;

代码展示

思路一:转化为寻找相交节点的相交点处:

c 复制代码
struct ListNode* getIntersectionNode(struct ListNode* headA, struct ListNode* headB)
{
    if(headA==NULL && headB==NULL)
    {
        return NULL;
    }
    struct ListNode* curA = headA, * curB = headB;
    int count1 = 0, count2 = 0;
    while (curA->next != NULL)
    {
        count1++;
        curA = curA->next;
    }
    while (curB->next != NULL)
    {
        count2++;
        curB = curB->next;
    }
    int gap = abs(count1 - count2);
    struct ListNode* longlist = headA;
    struct ListNode* shortlist = headB;
    if (count1 < count2)
    {
        longlist = headB;
        shortlist = headA;
    }
    if (curB == curA)
    {
        while (gap--)
        {
            longlist = longlist->next;
        }
        while (longlist != shortlist)
        {
            longlist = longlist->next;
            shortlist = shortlist->next;
        }
        return longlist;
            
    }
    else
    {
        return NULL;
    }
}
 
struct ListNode* getmeetnode(struct ListNode* head)
{
    struct ListNode* fast, * slow;
    fast = slow = head;
    while (fast && fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;
        if (fast == slow)
        {
            return fast;
        }
    }
    return NULL;
}
struct ListNode* detectCycle(struct ListNode* head)
{
    // 找到相遇点
    
    struct ListNode* meetnode = getmeetnode(head);
    if(meetnode == NULL)
    {
        return NULL;
    }
    // 断开为两个链表
    struct ListNode* cur1, *cur2;
    cur1 = meetnode->next;
    meetnode->next=NULL;
    cur2 = head;
 
    // 找到两个链表的交点
    struct ListNode* IntersectionNode = getIntersectionNode(cur1, cur2);
    return IntersectionNode;
 
}

思路二:利用数学推理巧妙的解决:

c 复制代码
typedef struct ListNode ListNode;
struct ListNode* detectCycle(struct ListNode* head) {
    ListNode* fast, * slow;
    fast = slow = head;
    while (fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if (slow == fast)
            break;
    }
    if (fast == NULL || fast->next == NULL)
    {
        return NULL;
    }
    ListNode* meet = fast;
    while (head != meet)
    {
        head = head->next;
        meet = meet->next;
    }
    return meet;
}

这两种方法都是可以通过leetcode的:


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