链表OJ之 快慢指针法总结

简介: 链表OJ之 快慢指针法总结

前言:

快慢指针指的是每次指针移动的步长,是解决链表相关的题目的一大利器,下面我将以例题的形式讲解快慢指针法。


目录

一. 链表的中间结点

思路:

代码实现:

二. 链表中倒数第k个结点

思路:

代码实现:

三.  判断链表中是否有环

思路:

代码实现:

四. 返回链表入环的第一个结点

思路:

代码实现:



一. 链表的中间结点


点我做题


思路:

创建两个快慢指针 slow , fast ,起始共同指向头节点,slow 每次走一步,fast 每次走两步,当 fast 为空或 fast 的下一个结点为空时,slow  即是中间节点的位置。

解释:

由于 fast 每次走两步,slow 每次走一步,slow 总是落后 fast 整体一半的长度最终 slow 理应为中间结点。

结点数为奇数:

c3c98b11ffcbdab15c8bb8a80e09eee2_e5fee39a347d40a09d77d7585bccb557.png

最终 fast 在最后一个结点,此时结束的标志为 fast->next == NULL;

结点数为偶数:

7e83dab41a0d6c0e33408779a90d2574_b3b729f33f71443e8f542634648c1ac8.png

最终 fast 在最后一个结点的下一个指向,此时的结束标志为 fast == NULL;

代码实现:

struct ListNode* middleNode(struct ListNode* head){
    struct ListNode* slow,*fast;
    slow = head;
    fast = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
    }
    return slow;
}


二. 链表中倒数第k个结点


点我做题


思路:

同样,创建两个快慢指针 slow , fast ,起始共同指向头节点,先让 fast 走 k 步,再让 fast 和 slow 同时前进,直到 fast 为空为止。

解释: 

先让 fast 走 k 步,那么 fast 与 slow 之间就隔了 k-1 个结点,fast 与 slow 同时前进,直到 fast 为空时,fast 与 slow 之间依然隔 k-1 个结点,那就是倒数第 k 个结点。

d6ba757874251c9b43debeefa218b1db_0ba264071d364608abda11e6eb5f5609.png

51190ed3f7a566aa6a4afd390e793545_e65134566b6a450aa46899616d92836c.png

代码实现:

int kthToLast(struct ListNode* head, int k){
    struct ListNode* fast,*slow;
    fast = slow = head;
    if(head == NULL)
    {
        return NULL;
    }
    //fast 前进 k 步
    while(k--)
    {
        fast = fast->next;
    }
    //slow 与 fast 共同前进
    while(fast)
    {
        slow = slow->next;
        fast = fast->next;
    }
    //注意返回的是整型数值
    return slow->val;
}


三.  判断链表中是否有环


点我做题


思路:

快慢指针 slow , fast,都从 head 开始,slow 一次走一步,fast 一次走两步,如果 slow 和 fast 能相遇,则链表有环。

解释:

主要是证明 有环情况下两个指针一定能相遇

fast 比 slow 先进入环,如图,假设 slow 和 fast 的位置,这两个指针之间差 N 步,

由于 fast 每次走两步,slow 每次走一步,所以 slow 和 fast 之间的距离每次缩短 1

6eee79c1522e498f4135704f7650d768_9596a125c87e46bf9697b514a7c971b2.png

N - 1

N - 2

N - 3

...

2

1

0    //此时两者相遇

证毕。 

代码实现:

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


四. 返回链表入环的第一个结点


点我做题


思路:

这里要先放一个结论:

在链表有环的情况下,一个指针在起始结点开始走,另一个结点在相遇点开始走,最终两个指针会在入环点相遇。

快慢指针 slow , fast,都从 head 开始,slow 一次走一步,fast 一次走两步,找到相遇点后,再让 start 与 meet 同时前进,两者相等的点即是入环点。

解释:

自然要证明上边的结论:

8f471cdae3c7375497c6b22eb866cf3f_fdce962440274c08bbd77f78abbb1115.png

在这里,我们设几个常量:

L:起始点到入环点的距离;

X:入环点到相遇点的距离;

C:环的周长。

已知条件:

slow 走的距离:L + X

fast 走的距离:L + n*C + X (n >=1)

fast 走的长度是 slow 走的长度的 2 倍。

推导:

fast 走的长度是 slow 走的长度的 2 倍 -->

2*(L + X)  == L + n*C + X (n >=1)

整理得:L == C - X + (n - 1)*C  (n >=1).

L == C - X + (n - 1)*C  (n >=1) 的解释:

e2958a10f20ff73feaa057565cdf054e_1f74f689ae034d148e4d245776277c0f.png

C - X + (n - 1)*C  (n >=1) 原本是 meet 到 innode 要走的所有可能距离,

L == C - X + (n - 1)*C  (n >=1) ,说明 start 到 innode 要走的距离与 meet 到 innode 要走的所有可能距离相等,所以两者相遇的点一定是 innode.

代码实现:

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* start = head;
            while(meet != start)
            {
                meet = meet->next;
                start = start->next;
            }
            return meet;
        }
    }
    return NULL;
}

总结:

快慢指针是解决链表问题的一大利器,建议多画图理解掌握。

目录
相关文章
|
4月前
链表指针的传参,传值和传地址
本文讨论了链表操作中指针传参的问题,特别是指针的传值与传地址的区别,并提供了修正代码,以确保链表插入操作能正确地修改指针指向的地址。
28 1
链表指针的传参,传值和传地址
|
3月前
|
算法 索引
单链表题+数组题(快慢指针和左右指针)
单链表题+数组题(快慢指针和左右指针)
51 1
|
4月前
|
存储 Java
数据结构第三篇【链表的相关知识点一及在线OJ习题】
数据结构第三篇【链表的相关知识点一及在线OJ习题】
45 7
|
4月前
|
存储
一篇文章了解区分指针数组,数组指针,函数指针,链表。
一篇文章了解区分指针数组,数组指针,函数指针,链表。
33 0
|
4月前
|
C语言
无头链表二级指针方式实现(C语言描述)
本文介绍了如何在C语言中使用二级指针实现无头链表,并提供了创建节点、插入、删除、查找、销毁链表等操作的函数实现,以及一个示例程序来演示这些操作。
56 0
|
7月前
【数据结构OJ题】环形链表
力扣题目——环形链表
55 3
【数据结构OJ题】环形链表
|
7月前
【数据结构OJ题】复制带随机指针的链表
力扣题目——复制带随机指针的链表
67 1
【数据结构OJ题】复制带随机指针的链表
|
7月前
【数据结构OJ题】环形链表II
力扣题目——环形链表II
49 1
【数据结构OJ题】环形链表II
|
7月前
【数据结构OJ题】相交链表
力扣题目——相交链表
46 1
【数据结构OJ题】相交链表
|
6月前
|
Python
【Leetcode刷题Python】138. 复制带随机指针的链表
LeetCode上题目“138. 复制带随机指针的链表”的Python解决方案,包括两种方法:一种是在每个节点后复制一个新节点然后再分离出来形成新链表;另一种是构建一个字典来跟踪原始节点与其副本之间的映射关系,从而处理新链表的构建。
33 1