利用快慢指针,求链表的中间结点,判断链表是否是环形链表

简介: 利用快慢指针,求链表的中间结点,判断链表是否是环形链表

前言

(1)在学习数据结构链表部分的时候,老师给出了几个题目。其中两个题目采用了快慢指针的技术,感觉有意思,于是写一篇博客记录一下。

快慢指针

(1)我们先来介绍一下快慢指针技术。这个说起来其实很简单,就是龟兔赛跑问题。

(2)兔子跑的比乌龟快,我们可以利用这个特性,来解决一些实际按理。


求链表的中间结点

原题链接

(1)原题链接:https://leetcode.cn/problems/middle-of-the-linked-list/description/

分析

(1)此题的核心目标是找到单链表的中间节点。如果是顺序表,就非常简单,直接采用sizeof()知道顺序表的大小,然后直接访问就可以了。

(2)但是单链表的存储不是连续的,想要找到某个元素,必须从头寻找。

(3)从头寻找又会有一个问题,因为这里是单链表,你只能往前走,不能回退。所以就算你遍历了整个链表,知道了链表的长度之后,还需要从头遍历链表,找到中间位置返回。假设这个链表长度是N,那么你就需要遍历1.5N次。

(4)这样虽然可行,但是很麻烦,因此我们可以采用快慢链表的方式,很好的解决这个问题。为什么这么说呢?因为我们需要找到单链表的中间位置,于是可以建立一个速度是乌龟一倍的兔子,如果兔子跑到了终点了,那么乌龟就正好是在中间位置。因此我们看下面这张图分析:

<1>假设这个单链表是偶数个,按照下面这个流程来,我们会发现跑的快的兔子最终会跑出赛道,而乌龟正好是在中间位置的第二个节点,完美符合要求。因此,单链表为偶数个,结束条件为fast==NULL。

<2>假设单链表是奇数个,按照下面流程来看,我们可以知道,兔子正好跑到终点,再跑就跑出赛道了。因此,单链表为奇数个,结束条件为fast–>==NULL。


代码

根据上面的解析,我们可以写出下面这段代码

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* middleNode(struct ListNode* head){
    struct ListNode* slow = head,*fast = head;
    while(fast != NULL && fast->next !=NULL)
    {
        slow = slow->next;
        fast = fast->next->next;
    }
    return slow;
}

判断链表是否是环形链表

原题链接

原题链接:https://leetcode.cn/problems/linked-list-cycle/description/

分析

(1)这个链表可能是一个环,我们需要进行判断,那么我们可以让兔子和乌龟在这个赛道里面跑。因为兔子比乌龟的速度快,所以这个环赛道里面,兔子铁定可以追上乌龟的。

(2)但是,各位需要注意一个点,只要时间足够,兔子在这个赛道一定可以追上乌龟。可是,兔子追上了乌龟一定会停下来吗?

(3)为什么需要问这个问题呢?因为,我们在编程的时候,都是先让兔子和乌龟跑一次,然后判断乌龟和兔子位置是否重叠。如果兔子和乌龟位置重叠,那么说明这是一个环形链表,如果兔子一直跑,发现能够跑到尽头,那么就说明这不是一个环形链表。

(4)那么我们想一下,如果这是一个环形链表,时间足够,兔子是肯定可以追上乌龟的,但是在进行判断的时候,兔子和乌龟的速度会重合吗?

(5)为了解决这个问题,我们可以画出下面这张图:

<1>假设乌龟进入环形区了,兔子肯定在这里面跑了一段时间了,现在是兔子追乌龟的过程。这个时候兔子距离乌龟n个元素。

<2>分析到这里,这个问题就很简单了,说白了就是一个小学的数学问题。因为兔子比乌龟速度要快,所以我们可以假设乌龟是不运动的,兔子的速度就是比乌龟快的那一部分。

<3>假设乌龟速度是每次走1个元素,兔子是每次走2个元素。那么按照上面分析,让乌龟不动,兔子移动,那么兔子的速度就是每次走一个元素。

<4>最终兔子走n次可以追上乌龟。

(6)看了上面的分析,肯定有同学会想,我们可不可以让兔子的速度比乌龟的再快一点呢?我要让乌龟的速度是1,兔子速度是3呢?

<1>现在我们假设这张图的环形是N个元素,乌龟到底环形区的时候,兔子距离乌龟n个元素。为了防止兔子追上了乌龟,但是越过了他,因此我们假设兔子要跑k圈最终才能和乌龟位置重合。

<2>根据上面的假设,我们可以建立下面这个数学模型计算出兔子要跑多少次才能追上乌龟。注意,k可以是0,1,2…的任意整数


1693298190613.png


<3>我们一直尝试k的值,最终计算出来的最小整数,就算兔子要跑的次数。

<4>上面我们假设了兔子每次跑3个元素,乌龟每次跑1个元素。因此fast-slow就是2。

<5>我们假设这个环形区N为4,乌龟到达环形区时候,兔子距离他的n为1。那么套入公式就是

1693298208942.png

<6>发现没有,上面这个条件下,永远不可能算出整数。因此,兔子比乌龟的速度快1个元素才能解决所有可能情况。

<7>这个时候,肯定又有人要说了,那我让兔子比乌龟的速度快1个元素,但是我让乌龟的速度提上来可以不。可以,当然可以,但是不推荐,你速度提上来了,但是程序运行速度就不一定提上来了,因为你乌龟看起来每次是多走了一点距离,但是对于程序而言,还是一步一步的走,不过不是每步都进行了一次判断而言。


代码

(1)根据上面的分析,我们可以写出如下代码

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
bool hasCycle(struct ListNode *head) {
    struct ListNode* slow = head,*fast = head;
    while(fast != NULL && fast->next != NULL)
    {
        slow = slow->next;
        fast = fast->next->next;
        if(slow==fast)
        {
            return true;
        }
    }
    return false;
}



目录
相关文章
|
2月前
|
算法
【❤️算法笔记❤️】-每日一刷-19、删除链表的倒数第 N个结点
【❤️算法笔记❤️】-每日一刷-19、删除链表的倒数第 N个结点
69 1
|
1月前
|
存储 算法 搜索推荐
链表的中间结点
【10月更文挑战第24天】链表的中间结点是链表操作中的一个重要概念,通过快慢指针法等方法可以高效地找到它。中间结点在数据分割、平衡检测、算法应用等方面都有着重要的意义。在实际编程中,理解和掌握寻找中间结点的方法对于解决链表相关问题具有重要价值。
18 1
|
1月前
|
算法 索引
单链表题+数组题(快慢指针和左右指针)
单链表题+数组题(快慢指针和左右指针)
39 1
|
3月前
链表的中间结点
链表的中间结点
181 57
|
2月前
|
存储
一篇文章了解区分指针数组,数组指针,函数指针,链表。
一篇文章了解区分指针数组,数组指针,函数指针,链表。
20 0
|
2月前
【LeetCode 09】19 删除链表的倒数第 N 个结点
【LeetCode 09】19 删除链表的倒数第 N 个结点
17 0
|
2月前
【数据结构】环形、相交、回文、分割、合并、反转链表
【数据结构】环形、相交、回文、分割、合并、反转链表
29 0
|
4月前
|
算法
LeetCode第19题删除链表的倒数第 N 个结点
该文章介绍了 LeetCode 第 19 题删除链表的倒数第 N 个结点的解法,通过使用快慢双指针,先将快指针移动 n 步,然后快慢指针一起遍历,直到快指针到达链尾,从而找到倒数第 N 个结点的前一个结点进行删除,同时总结了快慢指针可减少链表遍历次数的特点。
LeetCode第19题删除链表的倒数第 N 个结点
|
4月前
|
Python
【Leetcode刷题Python】138. 复制带随机指针的链表
LeetCode上题目“138. 复制带随机指针的链表”的Python解决方案,包括两种方法:一种是在每个节点后复制一个新节点然后再分离出来形成新链表;另一种是构建一个字典来跟踪原始节点与其副本之间的映射关系,从而处理新链表的构建。
23 1
|
4月前
|
存储 算法 数据处理
指针与链表
指针与链表
76 0