【5.7】指针算法-快慢指针解决环形链表

一、题目
给定一个链表,判断链表中是否有环。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引 从 0 开始)。如果 pos 是 -1,则在该链表中没有环。

示例 1
输入 : h e a d = [ 3 , 2 , 0 ,-4 ] , p o s = 1
输出 : tru e
解释 :链表中有一个环,其尾部连接到第二个节点。

示例 2
输入 : h e a d = [ 1 , 2 ] , p o s = 0
输出 : tru e
解释 :链表中有一个环,其尾部连接到第一个节点。


示例 3
输入 : h e a d = [ 1 ] , p o s = -1
输出 : f a l se
解释 :链表中没有环。

二、解题思路

判断链表是否有环可谓是一个常见的话题。其中,最简单的一种方式当属快慢指针法。慢指针每次前进一步,快指针每次前进两步。若二者相遇,则说明链表有环;若其中一个指针为空,则表明链表没有环。

为何快慢指针一定能判断是否有环呢?我们可以如此思考,倘若有环,那么快慢指针最终都会进入环上。假设环的长度为 m,快慢指针最近的间距是 n,如下图所示。

快指针每次前进两步,慢指针每次前进一步,所以每走一次,快慢指针的间距就会缩小一步。在图一中,走 n 次的时候二者会相遇;在图二中,走 m - n 次的时候二者会相遇。

三、代码实现

cpp 复制代码
#include <iostream>
#include <sstream>
#include <vector>

using namespace std;

// 定义链表节点结构
struct ListNode {
    int val;
    ListNode* next;
    ListNode(int x) : val(x), next(nullptr) {}
};

bool hasCycle(ListNode* head) {
    if (head == nullptr)
        return false;

    // 快慢两个指针
    ListNode* slow = head;
    ListNode* fast = head;

    while (fast != nullptr && fast->next != nullptr) {
        // 慢指针每次走一步
        slow = slow->next;
        // 快指针每次走两步
        fast = fast->next->next;
        // 如果相遇,说明有环,直接返回true
        if (slow == fast)
            return true;
    }

    // 否则就是没环
    return false;
}

// 辅助函数:根据输入字符串构建链表
ListNode* buildList(const string& input, int pos) {
    vector<int> values;
    stringstream ss(input);
    string item;

    // 解析输入字符串,提取链表节点的值
    while (getline(ss, item, ',')) {
        values.push_back(stoi(item));
    }

    // 构建链表
    ListNode* head = nullptr;
    ListNode* tail = nullptr;
    vector<ListNode*> nodes;

    for (int val : values) {
        ListNode* node = new ListNode(val);
        nodes.push_back(node);
        if (head == nullptr) {
            head = node;
            tail = node;
        } else {
            tail->next = node;
            tail = node;
        }
    }

    // 设置环
    if (pos != -1 && pos < nodes.size()) {
        tail->next = nodes[pos];
    }

    return head;
}

int main() {
    // 输入字符串
    string input = "3,2,0,-4";
    int pos = 1;

    // 构建链表
    ListNode* head = buildList(input, pos);

    // 检测链表是否有环
    bool result = hasCycle(head);

    // 打印结果
    cout << "Input: head = [" << input << "], pos = " << pos << endl;
    cout << "Output: " << (result ? "true" : "false") << endl;

    return 0;
}

来思索这样一个问题,在此处,快慢指针中快指针每次行进两步,慢指针每次走一步。倘若慢指针仍然每次走一步,快指针每次走三步的话,能否进行判断呢?又或者快指针每次走 m 步,慢指针每次走 n 步,并且 m 不等于 n,在这样的情形下能不能够进行判断呢?

在链表中判断是否有环的问题中,快慢指针是一种常用的方法。通常情况下,快指针每次走2步,慢指针每次走1步。我们来分析一下其他情况:

1. 慢指针每次走1步,快指针每次走3步

在这种情况下,快指针每次走3步,慢指针每次走1步。我们来分析一下是否能判断链表中是否有环。

分析:
  • 相遇条件:假设链表中有环,快指针和慢指针最终会在某个节点相遇。

  • 步数关系:快指针每次走3步,慢指针每次走1步。

  • 环的长度 :假设环的长度为L

  • 相遇点 :假设慢指针在环中走了k步,快指针在环中走了3k步。

结论:
  • 能判断:如果链表中有环,快指针和慢指针最终会在某个节点相遇。因为快指针每次走3步,慢指针每次走1步,快指针会比慢指针多走2步。如果链表中有环,快指针最终会追上慢指针。

2. 快指针每次走m步,慢指针每次走n步,并且m≠n

在这种情况下,快指针每次走m步,慢指针每次走n步,并且m ≠ n。我们来分析一下是否能判断链表中是否有环。

分析:
  • 相遇条件:假设链表中有环,快指针和慢指针最终会在某个节点相遇。

  • 步数关系 :快指针每次走m步,慢指针每次走n步。

  • 环的长度 :假设环的长度为L

  • 相遇点 :假设慢指针在环中走了k步,快指针在环中走了mk步。

结论:
  • 能判断 :如果链表中有环,快指针和慢指针最终会在某个节点相遇。因为快指针每次走m步,慢指针每次走n步,快指针会比慢指针多走m - n步。如果链表中有环,快指针最终会追上慢指针。

总结

  • 慢指针每次走1步,快指针每次走3步:能判断链表中是否有环。

  • 快指针每次走m步,慢指针每次走n步,并且m≠n:能判断链表中是否有环。

无论是快指针每次走3步,慢指针每次走1步,还是快指针每次走m步,慢指针每次走n步,并且m ≠ n,只要链表中有环,快指针最终会追上慢指针。因此,这些情况下都能判断链表中是否有环。

相关推荐
CoovallyAIHub2 小时前
港大&字节重磅发布DanceGRPO:突破视觉生成RLHF瓶颈,多项任务性能提升超180%!
深度学习·算法·计算机视觉
感哥3 小时前
C++ STL 常用算法
c++
CoovallyAIHub3 小时前
英伟达ViPE重磅发布!解决3D感知难题,SLAM+深度学习完美融合(附带数据集下载地址)
深度学习·算法·计算机视觉
saltymilk13 小时前
C++ 模板参数推导问题小记(模板类的模板构造函数)
c++·模板元编程
感哥13 小时前
C++ lambda 匿名函数
c++
沐怡旸19 小时前
【底层机制】std::unique_ptr 解决的痛点?是什么?如何实现?怎么正确使用?
c++·面试
感哥20 小时前
C++ 内存管理
c++
聚客AI20 小时前
🙋‍♀️Transformer训练与推理全流程:从输入处理到输出生成
人工智能·算法·llm
大怪v1 天前
前端:人工智能?我也会啊!来个花活,😎😎😎“自动驾驶”整起!
前端·javascript·算法
惯导马工1 天前
【论文导读】ORB-SLAM3:An Accurate Open-Source Library for Visual, Visual-Inertial and
深度学习·算法