讲师中心 微信公众号
AI工具推荐 视频效率加速

无向图中的环路检测:深度解析DFS与并查集算法

冬浩吖_4928

冬浩吖_4928

发布时间:2025-07-21 13:58:32

|

518人浏览过

|

来源于php中文网

原创

无向图中的环路检测:深度解析dfs与并查集算法

本文深入探讨了在无向图中检测环路的两种经典且高效的算法:深度优先搜索(DFS)和并查集(Union-Find)。通过详细阐述它们的核心思想、算法步骤及提供清晰的Java示例代码,文章旨在帮助读者理解如何在无向图中准确地识别环路,并分析了常见实现中的误区,为图算法的学习和实践提供了专业的指导。

理解无向图中的环

在图论中,环(Cycle)是指从图中的一个顶点出发,沿着边经过一系列不同的顶点,最终回到起始顶点的路径。在无向图中,由于边是双向的,因此需要特别注意“父子”关系,以避免将简单的回溯(即从子节点回到父节点)误判为环。检测无向图中的环是许多图算法的基础,例如判断图是否为树(无环连通图)。

方法一:基于深度优先搜索(DFS)的环路检测

深度优先搜索(DFS)是一种遍历图的算法,它从起始顶点开始,尽可能深地探索图的分支,直到达到死胡同或已访问的顶点,然后回溯。在无向图中,DFS检测环的核心思想是:如果在遍历过程中,我们遇到一个已经被访问过的顶点,并且这个顶点不是当前顶点的直接父节点,那么就存在一个环。

AI舌诊面诊问诊医疗系统+诊疗商城系统源码
AI舌诊面诊问诊医疗系统+诊疗商城系统源码

AI 舌诊面诊问诊医疗系统 + 诊疗商城系统源码是一套结合 AI 辅助诊断与商品转化的健康类程序,基于浩创云、ThinkPHP(PHP)开发,支持微信服务号与 H5 端,适合做中医舌诊面诊问答、健康建议推送以及配套诊疗商品的运营。

下载

核心思想

  1. 记录访问状态: 使用一个集合(或布尔数组)来记录每个顶点是否已被访问。
  2. 记录父节点: 在DFS递归调用中,传入当前节点的父节点。
  3. 判断环: 当DFS访问一个邻居顶点时:
    • 如果该邻居顶点未被访问,则继续对其进行DFS遍历,并将当前顶点设为它的父节点。
    • 如果该邻居顶点已被访问,并且它不是当前顶点的父节点,则说明找到了一条“回边”(Back Edge),即存在一个环。

算法步骤

  1. 初始化一个 visited 集合,用于记录已访问的节点。
  2. 初始化一个 parent 映射,用于记录DFS树中每个节点的父节点。
  3. 遍历图中的所有节点(以处理非连通图),对于每个未访问的节点,启动一次DFS。
  4. 在DFS函数 dfs(currentNode, parentNode, visited, parentMap) 中:
    • 将 currentNode 标记为已访问,并记录其父节点 parentNode。
    • 遍历 currentNode 的所有邻居 neighbor:
      • 如果 neighbor 是 parentNode,则跳过(这是无向图中的正常回溯边)。
      • 如果 neighbor 已经存在于 visited 集合中,则说明找到了一个环,返回 true。
      • 如果 neighbor 未被访问,则递归调用 dfs(neighbor, currentNode, visited, parentMap)。如果递归调用返回 true,则直接返回 true。
  5. 如果所有DFS遍历完成都没有找到环,则返回 false。

示例代码 (Java)

import java.util.*;

class DSGraphDFS {
    private Map<String, List<String>> adj; // 邻接列表表示图

    public DSGraphDFS() {
        adj = new HashMap<>();
    }

    // 添加无向边
    public void addEdge(String u, String v) {
        adj.computeIfAbsent(u, k -> new LinkedList<>()).add(v);
        adj.computeIfAbsent(v, k -> new LinkedList<>()).add(u); // 无向图,双向添加
    }

    /**
     * 检测无向图中是否存在环路 (DFS实现)
     * @return 如果存在环路,返回true;否则返回false。
     */
    public boolean hasCycleDFS() {
        Set<String> visited = new HashSet<>(); // 记录已访问的节点
        // 无需显式parent map,因为DFS函数参数中会传递父节点

        // 遍历所有节点,以处理非连通图
        for (String node : adj.keySet()) {
            if (!visited.contains(node)) {
                // 对于每个未访问的连通分量,启动DFS
                if (dfs(node, null, visited)) { // 初始调用时,父节点为null
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * DFS递归函数,用于检测环路
     * @param u 当前访问的节点
     * @param p u的父节点(在DFS遍历路径中)
     * @param visited 已访问节点集合
     * @return 如果在当前DFS路径中检测到环路,返回true;否则返回false。
     */
    private boolean dfs(String u, String p, Set<String> visited) {
        visited.add(u); // 标记当前节点为已访问

        // 遍历当前节点的所有邻居
        for (String v : adj.getOrDefault(u, Collections.emptyList())) {
            if (v.equals(p)) { // 如果邻居是父节点,跳过,因为这是无向图的正常回溯
                continue;
            }
            if (visited.contains(v)) { // 如果邻居已被访问,且不是父节点,则存在环
                return true; // 发现回边
            }
            // 如果邻居未被访问,则递归进行DFS
            if (dfs(v, u, visited)) {
                return true; // 递归调用中发现了环
            }
        }
        return false; // 当前路径没有发现环
    }

    // 辅助方法,用于获取所有节点,以便初始化DSGraphUnionFind
    public Set<String> getAllNodes() {
        return adj.keySet();
    }
}

注意事项

  • 处理非连通图: hasCycleDFS 方法外部的循环 for (String node : adj.keySet()) 是必要的,它确保即使图是非连通的,也能检测到所有连通分量中的环。
  • 父节点的重要性: 在无向图中,每条边 (u, v) 都可以双向遍历。为了避免将 v 返回到 u 误判为环,需要显式地传递并检查父节点。

方法二:基于并查集(Union-Find)的环路检测

并查集(Disjoint Set Union, DSU)是一种数据结构,用于管理一组不相交的集合。它支持两种主要操作:find(查找元素所属集合的代表元)和 union(合并两个集合)。在无向图中,并查集可以高效地检测环:当处理一条边 (u, v) 时,如果 u 和 v 已经属于同一个集合(即它们已经连通),那么添加这条边就会形成一个环。

核心思想

  1. 初始化: 将图中的每个顶点视为一个独立的集合。
  2. 处理边: 遍历图中的所有边。对于每条边 (u, v):
    • 使用 find 操作找到 u 和 v 所属集合的代表元。
    • 如果 u 和 v 的代表元相同,说明它们已经连通,添加这条边将形成一个环。
    • 如果 u 和 v 的代表元不同,说明它们不连通,使用 union 操作将它们所在的集合合并。

并查集基础

  • parent 数组/映射: 存储每个元素的父节点。如果 parent[i] == i,则 i 是其集合的代表元。
  • rank 或 size 数组/映射: 用于优化 union 操作,通过将较小的树连接到较大的树(按秩合并或按大小合并),保持树的平衡,从而降低树的高度,优化 find 操作的效率。
  • 路径压缩: find 操作的优化,在查找代表元的过程中,将路径上的所有节点的父节点直接指向代表元,从而扁平化树结构。

算法步骤

  1. 创建一个 DisjointSet 数据结构,初始化时将图中的每个节点都视为一个独立的集合。
  2. 遍历图中的所有边(每条边只处理一次,例如只处理 (u, v) 而不处理 (v, u))。
  3. 对于每条边 (u, v):
    • 调用 ds.find(u) 找到 u 的代表元 rootU。
    • 调用 ds.find(v) 找到 v 的代表元 `rootV

热门AI工具

更多
DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

切问学术

切问学术是一款AI论文写作工具,复旦大学NLP团队推出的AI学术智能体。

蛙蛙写作

一款AI论文写作工具,主要用于超级AI智能写作助手,适合需要提升相关任务效率的用户。

Laper
Laper Hot

Laper是专为编剧、导演和制片人推出的 AI 原生剧本创作工具。

讯飞智作

讯飞智作是一款AI视频创作工具,AI文本配音工具,数字人课程、营销视频制作。

UP简历
UP简历 Hot

一款AI办公效率工具,主要用于基于AI技术的免费在线简历制作工具,适合需要提升相关任务效率的用户。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

超级麦吉

一款通过数字员工协作完成调研、数据分析与文档制作的AI办公平台。

相关专题

更多
edge是什么浏览器
edge是什么浏览器

Edge是一款由Microsoft开发的网页浏览器,是Windows 10操作系统中默认的浏览器,其目标是提供更快、更安全、更现代化的浏览器体验。本专题为大家提供edge浏览器相关的文章、下载、课程内容,供大家免费下载体验。

3022

2023.08.21

IE浏览器自动跳转EDGE如何恢复
IE浏览器自动跳转EDGE如何恢复

ie浏览器自动跳转edge的解决办法:1、更改默认浏览器设置;2、阻止edge浏览器的自动跳转;3、更改超链接的默认打开方式;4、禁用“快速网页查看器”;5、卸载edge浏览器;6、检查第三方插件或应用程序等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2794

2024.03.05

如何解决Edge打开但没有标题的问题
如何解决Edge打开但没有标题的问题

若 Microsoft Edge 浏览器打开后无标题(窗口空白或标题栏缺失),可尝试以下方法解决: 重启 Edge:关闭所有窗口,重新启动浏览器。 重置窗口布局:右击任务栏 Edge 图标 → 选择「最大化」或「还原」。 禁用扩展:进入 edge://extensions 临时关闭插件测试。 重置浏览器设置:前往 edge://settings/reset 恢复默认配置。 更新或重装 Edge:检查最新版本,或通过控制面板修复

3172

2025.04.24

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

6759

2023.08.02

c语言union的用法
c语言union的用法

c语言union的用法是一种特殊的数据类型,它允许在相同的内存位置存储不同的数据类型,union的使用可以帮助我们节省内存空间,并且可以方便地在不同的数据类型之间进行转换。使用union时需要注意对应的成员是有效的,并且只能同时访问一个成员。本专题为大家提供union相关的文章、下载、课程内容,供大家免费下载体验。

749

2023.09.27

treenode的用法
treenode的用法

​在计算机编程领域,TreeNode是一种常见的数据结构,通常用于构建树形结构。在不同的编程语言中,TreeNode可能有不同的实现方式和用法,通常用于表示树的节点信息。更多关于treenode相关问题详情请看本专题下面的文章。php中文网欢迎大家前来学习。

2661

2023.12.01

C++ 高效算法与数据结构
C++ 高效算法与数据结构

本专题讲解 C++ 中常用算法与数据结构的实现与优化,涵盖排序算法(快速排序、归并排序)、查找算法、图算法、动态规划、贪心算法等,并结合实际案例分析如何选择最优算法来提高程序效率。通过深入理解数据结构(链表、树、堆、哈希表等),帮助开发者提升 在复杂应用中的算法设计与性能优化能力。

376

2025.12.22

深入理解算法:高效算法与数据结构专题
深入理解算法:高效算法与数据结构专题

本专题专注于算法与数据结构的核心概念,适合想深入理解并提升编程能力的开发者。专题内容包括常见数据结构的实现与应用,如数组、链表、栈、队列、哈希表、树、图等;以及高效的排序算法、搜索算法、动态规划等经典算法。通过详细的讲解与复杂度分析,帮助开发者不仅能熟练运用这些基础知识,还能在实际编程中优化性能,提高代码的执行效率。本专题适合准备面试的开发者,也适合希望提高算法思维的编程爱好者。

417

2026.01.06

Vite 项目如何打包和部署
Vite 项目如何打包和部署

Vite项目打包部署专题汇集生产构建、产物预览、输出目录、资源路径、Nginx与GitHub Pages部署及常见白屏和路由404问题的已审核文章,帮助读者完成上线流程。

20

2026.10.28

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn