力扣 快慢指针

1 环形链表

141. 环形链表 - 力扣(LeetCode)

定义两个指针,一快一慢。慢指针每次只移动一步,而快指针每次移动两步。初始时,慢指针和快指针都在位置 head,这样一来,如果在移动的过程中,快指针反过来追上慢指针,就说明该链表为环形链表。

使用 do-while 循环,可以把快慢指针的初始值都置为 head

cpp 复制代码
class Solution {
public:
    bool hasCycle(ListNode *head) {
        ListNode* low = head, *fast = head;
        do {
            if(!fast || !fast->next)
                return false;
            low = low->next;
            fast = fast->next->next;
        }while(low != fast);

        return true;
    }
};

2 删除链表的倒数第 N 个结点

19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)

快指针先走n步,慢指针后走(k-n步)。

cpp 复制代码
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        // 由于可能会删除链表头部,用哨兵节点简化代码
        ListNode* res = new ListNode(0, head);
        ListNode* low = res, *fast = res;

        while(n--) {
            fast = fast->next;
        }

        while(fast->next) {
            fast = fast->next;
            low = low->next;
        }

        // 左指针的下一个节点就是倒数第 n 个节点
        low->next = low->next->next;

        return res->next; 
    }
};

3 环形链表II

142. 环形链表 II - 力扣(LeetCode)

参考题解:Krahets - 力扣(LeetCode)

使用快慢指针

  • fast 指针走过链表末端,说明链表无环,此时直接返回 null

  • fast == slow时,有环,且两指针在环中第一次相遇。

    将以上两式相减得到s = nb而环的入口的表达式为: a + nb a 即从 head 出发走到环入口的距离(步数), nb 即绕了几圈。又 s = nb ,即慢指针再走 a 步即可到达环入口。

    • fast 走的步数是 slow 步数的 2 倍,即 f = 2s;(解析: fast 每轮走 2 步)

    • fast slow 多走了 n 个环的长度,即 f = s + nb;( 解析: 双指针在环内绕圈直到重合,重合时 fast slow 多走环的长度整数倍 )。

  • fast 重新指向链表头部节点,此时 f = 0s=nb,令slow fast 同时每轮向前走 1 步。当两指针重合时,说明fastslow都满足a + nb。 即fast 指针走到f = aslow 指针走到 s = a + nb

  • 最后返回slowfast即可。

cpp 复制代码
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode* slow = head, *fast = head;
        do {
            if(!fast || !fast->next)
                return NULL;
            fast = fast->next->next;
            slow = slow->next;
        } while(fast != slow);

        // 此时 f = 2s, f = s + nb
        // 所以 s = nb
        // 而环入口节点表达式:a + nb
        
        // 所以让其在入口处汇合:
        fast = head;
        while(slow != fast) {
            fast = fast->next;
            slow = slow->next;
        }
        return slow;
    }
};
相关推荐
武子康4 分钟前
大数据-276 Spark MLib - 基础介绍 机器学习算法 Bagging和Boosting区别 GBDT梯度提升树
大数据·人工智能·算法·机器学习·语言模型·spark-ml·boosting
武子康7 分钟前
大数据-277 Spark MLib - 基础介绍 机器学习算法 Gradient Boosting GBDT算法原理 高效实现
大数据·人工智能·算法·机器学习·ai·spark-ml·boosting
啾啾Fun17 分钟前
【Java微服务组件】分布式协调P4-一文打通Redisson:从API实战到分布式锁核心源码剖析
java·redis·分布式·微服务·lua·redisson
消失的旧时光-194332 分钟前
Android USB 通信开发
android·java
惊鸿一博34 分钟前
java_网络服务相关_gateway_nacos_feign区别联系
java·开发语言·gateway
Andrew_Xzw1 小时前
数据结构与算法(快速基础C++版)
开发语言·数据结构·c++·python·深度学习·算法
超的小宝贝2 小时前
数据结构算法(C语言)
c语言·数据结构·算法
凤年徐4 小时前
【数据结构初阶】单链表
c语言·开发语言·数据结构·c++·经验分享·笔记·链表
朝新_7 小时前
【多线程初阶】阻塞队列 & 生产者消费者模型
java·开发语言·javaee