【数据结构实战】判断链表是否有环:快慢指针法(Floyd 判圈算法)

一、问题描述

给定一个单向链表,判断链表中是否存在环

  • 如果链表有环:输出「有环」
  • 如果链表无环:输出「无环」

什么是环?链表最后一个节点的 next 不再指向 NULL,而是指向链表前面的某个节点,形成闭环。


二、解题思路:快慢指针法(Floyd 判圈算法)

这是最经典、最常用、空间复杂度 O (1) 的解法。

核心思想

  1. 定义两个指针:
    • 慢指针 slow :每次走 1 步
    • 快指针 fast :每次走 2 步
  2. 从头节点一起出发
  3. 如果有环 :快指针一定会在环里追上并相遇慢指针
  4. 如果无环:快指针会先走到 NULL,结束遍历
  5. 图解如下:

为什么一定会相遇?

可以想象成:在环形跑道上跑步,一个快一个慢,只要跑道是环,快的一定会套圈追上慢的


三、核心代码

1. 链表节点结构

cpp 复制代码
typedef struct Node {
    int data;           // 数据域
    struct Node *next;  // 指针域
} Node;

2. 判断是否有环(核心函数)

cpp 复制代码
int isCycle(Node *head) {
    Node *fast = head;
    Node *slow = head;

    while (fast != NULL && fast->next != NULL) {
        slow = slow->next;        // 慢指针走1步
        fast = fast->next->next;  // 快指针走2步

        if (fast == slow) {       // 相遇 = 有环
            return 1;
        }
    }
    return 0;  // 快指针到末尾 = 无环
}

关键点:

  • 循环条件必须判断 fast != NULL && fast->next != NULL,防止越界
  • 相遇就直接返回有环,效率很高

四、完整可运行代码(C 语言)

创建链表、构造环、测试、输出结果

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>

// 1. 链表节点定义
typedef struct Node {
    int data;
    struct Node *next;
} Node;

// 2. 创建头节点
Node *initList() {
    Node *head = (Node*)malloc(sizeof(Node));
    head->next = NULL;
    return head;
}

// 3. 尾插法添加节点
Node *insertTail(Node *tail, int data) {
    Node *newNode = (Node*)malloc(sizeof(Node));
    newNode->data = data;
    newNode->next = NULL;
    tail->next = newNode;
    return newNode;
}

// 4. 获取尾节点
Node *getTail(Node *head) {
    Node *p = head;
    while (p->next != NULL) {
        p = p->next;
    }
    return p;
}

// 5. 判断是否有环(核心)
int isCycle(Node *head) {
    Node *fast = head;
    Node *slow = head;

    while (fast != NULL && fast->next != NULL) {
        slow = slow->next;
        fast = fast->next->next;

        if (fast == slow) {
            return 1;
        }
    }
    return 0;
}

// 6. 主函数测试
int main() {
    Node *head = initList();
    Node *tail = getTail(head);

    // 插入数据:1 2 3 4 5 6 7 8
    tail = insertTail(tail, 1);
    tail = insertTail(tail, 2);
    tail = insertTail(tail, 3);
    Node *node3 = tail;  // 保存3号节点
    tail = insertTail(tail, 4);
    tail = insertTail(tail, 5);
    tail = insertTail(tail, 6);
    tail = insertTail(tail, 7);
    tail = insertTail(tail, 8);

    // 构造环:8 -> 3
    tail->next = node3;

    // 判断并输出
    if (isCycle(head)) {
        printf("运行结果:该链表【有环】\n");
    } else {
        printf("运行结果:该链表【无环】\n");
    }

    return 0;
}

代码中构造了 1→2→3→4→5→6→7→8→3 的环形链表,isCycle 函数检测到快慢指针相遇,因此输出有环

五、运行结果

编译运行后,控制台输出:

如果你把构造环的代码注释掉:

cpp 复制代码
// tail->next = node3;

再次运行,结果变为:


六、总结

  1. 慢指针 1 步,快指针 2 步
  2. 相遇 = 有环,快指针到 NULL = 无环
  3. 时间复杂度 O (n),空间复杂度 O (1)
相关推荐
无相求码4 分钟前
指针的5层境界,从入门到劝退:C语言指针进阶全解析
c语言
zander25821 分钟前
114. 二叉树展开为链表
数据结构·链表·深度优先
变量未定义~42 分钟前
最小生成树1(Prim模板)、最小生成树2(kruskal模板)、最近公共祖先(模板)
算法
这就是佬们吗1 小时前
Python入门⑤-异常处理、文件操作与实战项目
开发语言·数据库·python·算法·pycharm
LONGZETECH1 小时前
新能源汽车充电设备装配与调试仿真教学软件 技术架构与核心实现解析
大数据·算法·3d·unity·架构·汽车
自学小白菜1 小时前
关于提升机器学习算法做预测回归任务精度的方法分享
算法·机器学习·回归·提升精度
C++ 老炮儿的技术栈1 小时前
基于MFC+原生GDI手动自绘仪表盘控件
c语言·c++·visual studio·控件·工业控制·gdi·自绘
学废了wuwu1 小时前
从入门到实战:深入浅出模型压缩三大核心——剪枝、彩票假设与知识蒸馏
人工智能·算法·剪枝
小肝一下1 小时前
3. 单链表
c语言·数据结构·c++·算法·leetcode·链表·dijkstra
劈星斩月1 小时前
监督学习算法 之 决策树
学习·算法·决策树