【每日算法 && 数据结构(C++)】—— 05 | 判断单链表是否有环(解题思路、流程图、代码片段)


在这里插入图片描述

The future belongs to those who believe in the beauty of their dreams.

未来属于那些相信梦想之美的人

01 | 👑 题目描述

给你一个单链表,请判断其中是否存在环

单链表是一种常见的数据结构,用于存储和组织数据元素。它由一系列节点(Node)组成,每个节点包含一个数据元素和一个指向下一个节点的指针(通常称为next指针)。

单链表的特点是每个节点只能访问其后继节点,而无法直接访问前驱节点或任意位置的节点。链表的头节点即为链表的入口点,尾节点的next指针为空(或指向null),表示到达了链表的末尾。

以下是一个简单的单链表示例:

Head -> Node1 -> Node2 -> Node3 -> ... -> NodeN -> null

在这个示例中,Head是链表的头节点,Node1Node2Node3等分别是链表中的节点,最后的null表示链表结束。

单链表的插入和删除操作相对灵活,可以在常数时间(O(1))内进行。但是,由于单链表无法直接访问前驱节点,如果需要在链表中查找、删除或修改某个节点,通常需要从头节点开始遍历链表,直到找到目标节点。

单链表具有动态性和节省空间的优势。由于节点在内存中可以不连续存储,因此可以根据需要动态地分配和释放节点空间,有效利用内存资源。

判断单链表是否有环是指判断链表中是否存在一个循环,即链表中某个节点的 next 指针指向之前已经出现过的节点,形成一个环状结构

02 | 🔋 解题思路

常见的判断单链表是否有环的方法是使用快慢指针双指针)的方法
具体步骤如下

  1. 定义两个指针,一个慢指针和一个快指针,初始时都指向链表的头节点。
  2. 慢指针每次移动一步,快指针每次移动两步。
  3. 如果链表中不存在环,则快指针会先到达链表的尾部(即指向空节点),此时可以判断链表无环。
  4. 如果链表中存在环,那么快指针最终会追上慢指针,二者会相遇。
  5. 如果在遍历过程中出现了相遇,则可以判断链表有环。

在这里插入图片描述

原理
当链表中存在环时,快指针每次移动两步,而慢指针每次移动一步,快指针相对于慢指针的速度差是1步。假设链表中的环有n个节点,当慢指针进入环后,快指针与慢指针之间的距离会逐渐减小,直到最终相遇。而快指针每次移动两步,所以最终一定会追上慢指针。
下面是一个示例:

1 -> 2 -> 3 -> 4 -> 5 -> 2   (存在环)

在这个示例中,链表中的节点2被重复访问,形成了一个环。使用快慢指针法时,快指针会先进入环(比如指向节点4),而慢指针稍后也会进入环。最终,快指针会追上慢指针,二者在节点2相遇。

因此,通过这种方法可以判断一个单链表是否有环。如果两个指针相遇,则链表有环;如果快指针先到达链表尾部(即指向空节点),则链表无环。

时间 && 空间复杂度

  • 时间复杂度O(n),其中 n 是链表中节点的个数
    • 快指针每次移动两步,慢指针每次移动一步,当链表中不存在环时,快指针会先到达链表末尾(null),此时需要遍历 n/2 个节点。
    • 当链表中存在环时,快指针会在环内循环前进,直到追上慢指针。这个过程最多需要遍历环的长度 k(环中节点的个数)。
    • 因此,算法的时间复杂度为 O(n + k),其中 n 是链表中节点的个数,k 是环中节点的个数。
  • 空间复杂度O(1)
    因为只使用了常数级别的额外空间来存储慢指针和快指针

03 | 🧢 代码片段

#include <iostream>

using namespace std;

struct ListNode {
    int val;
    ListNode* next;
    ListNode(int x) : val(x), next(nullptr) {}
};

bool hasCycle(ListNode* head) {
    if (head == nullptr || head->next == nullptr) {
        return false;  // 空链表或只有一个节点的链表肯定没有环
    }
    
    ListNode* slow = head;  // 慢指针
    ListNode* fast = head;  // 快指针
    
    while (fast && fast->next) {
        slow = slow->next;          // 慢指针每次向后移动一步
        fast = fast->next->next;    // 快指针每次向后移动两步
        
        if (slow == fast) {
            return true;  // 快慢指针相遇,说明链表有环
        }
    }
    
    return false;  // 快指针到达链表末尾,说明链表无环
}

int main() {
    // 创建一个有环的链表
    ListNode* head = new ListNode(1);
    ListNode* node2 = new ListNode(2);
    ListNode* node3 = new ListNode(3);
    ListNode* node4 = new ListNode(4);
    ListNode* node5 = new ListNode(5);

    head->next = node2;
    node2->next = node3;
    node3->next = node4;
    node4->next = node5;
    node5->next = node2;  // 环的入口在节点2

    // 判断链表是否有环
    bool hasCycleFlag = hasCycle(head);
    
    if (hasCycleFlag) {
        cout << "链表有环" << endl;
    } else {
        cout << "链表无环" << endl;
    }
    
    // 释放链表内存
    delete head;
    delete node2;
    delete node3;
    delete node4;
    delete node5;

    return 0;
}

在这里插入图片描述

在这里插入图片描述

各位大佬点点关注,点赞,收藏,有空的时候再回来看看,谢谢
  • 11
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 17
    评论
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ltd Pikashu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值