Leetcode刷题-142: 环形链表 II

给定一个链表,判断链表中是否有环。如果链表中有环,返回环的起始节点;否则,返回null。通过哈希集合辅助法和快慢指针两种方法进行解决。
摘要由CSDN通过智能技术生成

1.题目描述

给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。不允许修改 链表。
示例1:
在这里插入图片描述

输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。

示例2:

输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。

示例3:

输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。

来源:力扣(LeetCode)

2.题目分析

2.1 哈希集合辅助法

思想比较简单,就是依次遍历链表,每次遍历到一个元素先检查这个节点是否已经遍历过,如果已经遍历过就说明碰到的环,又因为遍历是从前到后的,所以第一次遇到的已经存在的节点肯定就是环的起点,此时返回结果退出程序。如果该节点不存在于哈希表中,说明还没碰到环,那么继续遍历下一个节点知道为空。

2.2 快慢指针

2.2.1 快慢指针的工作

  • 一般我们使用的快慢指针都是二倍速的,这么说吧,如果有小敏和小明两个同学,小明是个喜欢运动的同学所以跑的比较快,有多快呢,他的速度是小敏的两倍,假如他俩一起在操场跑圈的话那么他们肯定在某个时间点相遇。类比到指针也是一样的,如果我们设置两个快和慢的指针fPtrsPtr指向链表头部,如果链表存在环,那么它们一定会再次相遇的。在这道题中我们要求的就是环的起点。讲个小故事:

小明和小敏在晚饭后同时从食堂出发跑到操场跑圈,从一开始小明就是以小敏的二倍速开始跑,小敏到操场之后跑了小半圈又发现小明跟她相遇了,觉得这个人长得还挺帅于是跟他打招呼,说道:“好巧啊,我们又碰到了”,小明是个钢铁直男,严肃地说道:“这有什么巧的,我比你跑的快两倍”,此时我们又在绕着一个圈跑,那肯定会遇到啊”。小敏略感无语,不知道怎么回答他,然后小明继续说道,”你看你从食堂到这里跑的路程是s1,我现在跑的路程是s2,但是这里的s1*2 = s2 ”。小敏明显感觉有点不适,但是小明还是自顾自地说道:“不要小瞧这个呀,这里面有奇妙的数学知识呢”。小敏不耐烦地问道:“那是什么呢?”。小明回答道:“你看假如从食堂到操场的距离为a,操场一圈是c,而你在操场现在第一圈已经跑了b的距离,然后我是比你多跑一圈但是我跑的总距离是你的两倍,这时候就有2(a+b) = a+b+c;进而就会得出a+b=c是不是感觉很有规律呢?”。小敏感觉这是个钢铁直男不再跟他说话了。小明又兴奋地说道:“其实还可以根据这个算出操场的入口地点呢”。小敏感觉很奇怪,入口地点不就在那里吗,还用求嘛?但是小明明显是陷入数学等式,又自顾自地说到,此时我们得出了a+b=c,说明你现在距离操场入口的距离是c-b=a,跟食堂到这里的距离一样呢,所以找个人跟你以同样的速度从食堂开始跑到这里,你们相遇的地方就是操场入口。

回到问题本身,如果使用快慢指针的话我们可以明确的确定链表是否有环,但是如何确定环的起点呢?我们将上面的例子归化到链表中来分析,假设有下面的链表
dow_50,text_Q1NETiBASVRTT0tfVQ==,size_20,color_FFFFFF,t_70,g_se,x_16)

  • 我们分别设置一个快慢指针fPtrsPtr指向节点6,让他们分别每次向前两个和一个节点。我们会发现它们的第一次相遇总是会在环内的。若相遇,此时的快指针fPtr一定比慢指针sPtr多走了一个环的长度c,设原点到环口要a步,从环口到相遇要b步,则相遇时sPtr走了a+b步,fPtr走了2*(a+b)步也就是a+b+c步,所以有2(a+b) = a+b+c,那么我们就可以得出a+b=c
    在这里插入图片描述
  • 此时根据上面的式子,我们发现b这个点很神奇,它距离环入口的距离就是原点到环入口的距离。用数学等式证明就是,设从相遇位置到环口为d,则b+d=c,则a=d,这时候要求a,就让一个指针从起点出发,另一个相遇位置出发,再次相遇就是环口。

3.题目解答

3.1 哈希集合辅助求解

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
    	//哈希集合存储已经遍历过的节点
        unordered_set<ListNode*> visited;
        ListNode* cur = head;
        while(cur!=nullptr){
            if(visited.count(cur)==0){
                visited.insert(cur);
            }
            else return cur;
            cur = cur->next;
        }
        return nullptr;
    }
};

3.2 快慢指针

 ListNode *detectCycle(ListNode *head) {
        if(head == nullptr || head->next == nullptr){
            return nullptr;
        }
        //分别设置慢指针快指针
        ListNode* sPtr = head;
        ListNode* fPtr = head;
        while(fPtr != nullptr && fPtr->next!=nullptr){
            sPtr = sPtr->next;
            fPtr = fPtr->next->next;
            //若快慢指针相遇则说明有环
            //条件b+d=c=a+b
            while(sPtr == fPtr){
                ListNode* cur1 = fPtr;
                ListNode* cur2 = head;
                //根据数学条件得出相遇点到环入口的距离等于起点到入口的距离
                while(cur1 != cur2){
                    cur1=cur1->next;
                    cur2=cur2->next;
                }
                //再次相遇则为入口
                return cur2;
            }
        }
        return nullptr;
    }

总结:快慢指针

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值