【算法基础实验】图论-基于DFS的连通性检测

2024-04-27 07:04

本文主要是介绍【算法基础实验】图论-基于DFS的连通性检测,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

基于DFS的连通性检测

理论基础

在图论中,连通分量是无向图的一个重要概念,特别是在处理图的结构和解析图的组成时。连通分组件表示图中的一个子图,在这个子图中任意两个顶点都是连通的,即存在一条路径可以从一个顶点到达另一个顶点,并且这个子图是最大的,即不能通过添加更多的顶点来增加连通性。对于有向图,这通常被称为强连通分量。

基于DFS的连通分量算法

书中4.1.6节提到的基于深度优先搜索(DFS)的连通分量算法用于识别和处理无向图中的连通分量。这个算法的基本思想是使用DFS遍历图中的每个顶点,同时记录哪些顶点是连通的。

算法步骤

  1. 初始化:为每个顶点准备一个标记数组 marked[] 来记录每个顶点是否被访问过,另外用一个数组 id[] 来记录每个顶点所属的连通分量的标识符。还需要一个计数器 count 来统计连通分量的数量。
  2. DFS遍历:从任意未被访问的顶点开始,执行DFS遍历。在遍历过程中,标记所有可达的顶点为已访问,同时将这些顶点的 id[] 设置为当前的连通分量标识符。
  3. 连通分量标识:每次在DFS遍历开始前增加连通分量计数器 count,并将遍历过程中访问的所有顶点的连通分量标识设置为这个计数器的值。
  4. 重复执行:重复上述过程,直到图中的所有顶点都被访问过。

应用

  • 图的结构分析:识别图中的独立部分或者紧密相关的群组。
  • 网络设计:确定网络中的独立组件,以优化设计和提高稳定性。
  • 社交网络:识别社交网络中的社区或者群组。

通过这种基于DFS的连通分量算法,可以有效地解析和处理图的结构,对于复杂网络的分析尤其有用。

数据结构

private boolean[] marked
private int[] id
private int count
myBag
myGraph

实验数据和算法流程

这里使用tinyG.txt来构成实验用的无向图

注意算法流程中count,marked[],id[]的变化

请添加图片描述

代码实现

import edu.princeton.cs.algs4.In;
import edu.princeton.cs.algs4.StdOut;public class myCC {private boolean[] marked;private int[] id;private int count;public myCC(myGraph G){marked = new boolean[G.V()];id = new int[G.V()];for(int s=0;s<G.V();s++){if(!marked[s]){dfs(G,s);//这里是精髓所在,每次dfs回到这里就说明互相连通的一组顶点已经完成遍历,//也就确定了一个连通分量count++;                }}}private void dfs(myGraph G, int v){marked[v] = true;id[v] = count;for(int w:G.adj(v)){if(!marked[w]){dfs(G,w);}}}public boolean connected(int v, int w){return id[v]==id[w];}public int id(int v){return id[v];}public int count(){return count;}public static void main(String[] args){myGraph G = new myGraph(new In(args[0]));myCC cc = new myCC(G);int M = cc.count();StdOut.println(M + " components");myBag<Integer>[] components = (myBag<Integer>[]) new myBag[M];for(int i=0;i<M;i++){components[i] = new myBag<Integer>();}for(int v=0;v<G.V();v++){components[cc.id(v)].add(v);}for(int i=0;i<M;i++){for(int v:components[i]) StdOut.print(v+" ");StdOut.println();}}
}

代码详解

这段代码实现了一个基于深度优先搜索(DFS)的连通分量(CC)类 myCC,用于确定无向图中所有的连通分量。下面是详细的代码解释:

类定义和变量


public class myCC {private boolean[] marked;  // 标记数组,用于标记每个顶点是否已经被访问过private int[] id;          // 每个顶点所属的连通分量标识private int count;         // 连通分量的数量
  • marked 数组用于记录图中的每个顶点是否已经被访问。
  • id 数组用于存储每个顶点所属的连通分量的ID。
  • count 用于计数图中连通分量的总数。

构造函数


public myCC(myGraph G){marked = new boolean[G.V()];id = new int[G.V()];for(int s = 0; s < G.V(); s++) {if (!marked[s]) {dfs(G, s);count++;  // 完成一个连通分量的搜索后,增加连通分量的计数}}
}

构造函数遍历图中的所有顶点,对于每个未标记的顶点,执行DFS来标记和记录所有能从该顶点访问到的顶点,这些顶点构成一个连通分量。每次DFS调用结束后,连通分量数 count 加一。

DFS 方法


private void dfs(myGraph G, int v){marked[v] = true;id[v] = count;for (int w : G.adj(v)) {if (!marked[w]) {dfs(G, w);}}
}

dfs 方法标记顶点 v 为已访问,并将其连通分量ID设置为当前的 count。然后递归地访问所有与顶点 v 直接相连的未标记顶点。

连通分量的辅助方法


public boolean connected(int v, int w) { return id[v] == id[w]; }
public int id(int v) { return id[v]; }
public int count() { return count; }

这些方法提供了:

  • connected(v, w) 检查两个顶点是否属于同一个连通分量。
  • id(v) 返回顶点 v 的连通分量ID。
  • count() 返回图中连通分量的总数。

主方法


public static void main(String[] args){myGraph G = new myGraph(new In(args[0]));myCC cc = new myCC(G);int M = cc.count();StdOut.println(M + " components");myBag<Integer>[] components = (myBag<Integer>[]) new myBag[M];for (int i = 0; i < M; i++) {components[i] = new myBag<Integer>();}for (int v = 0; v < G.V(); v++) {components[cc.id(v)].add(v);}for (int i = 0; i < M; i++) {for (int v : components[i]) StdOut.print(v + " ");StdOut.println();}
}

主方法使用 myCC 类来处理一个从文件读取的图,并输出所有的连通分量。这里,连通分量被存储在一个 myBag 数组中,每个 myBag 对象存储一个连通分量的所有顶点,然后输出每个连通分量的顶点。

这段代码是一个完整的图连通分量识别实现,使用DFS作为基本的遍历策略。

实验

代码编译

javac myCC.java

运行代码

将实验数据tinyG.txt导入代码后,myCC可以检测到3个连通分量,并逐行将连通分量中的元素打印出来

java myCC ..\data\tinyG.txt               
3 components
6 5 4 3 2 1 0
8 7
12 11 10 9

参考资料

算法(第4版)人民邮电出版社

这篇关于【算法基础实验】图论-基于DFS的连通性检测的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/939813

相关文章

SpringBoot实现MD5加盐算法的示例代码

《SpringBoot实现MD5加盐算法的示例代码》加盐算法是一种用于增强密码安全性的技术,本文主要介绍了SpringBoot实现MD5加盐算法的示例代码,文中通过示例代码介绍的非常详细,对大家的学习... 目录一、什么是加盐算法二、如何实现加盐算法2.1 加盐算法代码实现2.2 注册页面中进行密码加盐2.

C#基础之委托详解(Delegate)

《C#基础之委托详解(Delegate)》:本文主要介绍C#基础之委托(Delegate),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1. 委托定义2. 委托实例化3. 多播委托(Multicast Delegates)4. 委托的用途事件处理回调函数LINQ

Java时间轮调度算法的代码实现

《Java时间轮调度算法的代码实现》时间轮是一种高效的定时调度算法,主要用于管理延时任务或周期性任务,它通过一个环形数组(时间轮)和指针来实现,将大量定时任务分摊到固定的时间槽中,极大地降低了时间复杂... 目录1、简述2、时间轮的原理3. 时间轮的实现步骤3.1 定义时间槽3.2 定义时间轮3.3 使用时

如何通过Golang的container/list实现LRU缓存算法

《如何通过Golang的container/list实现LRU缓存算法》文章介绍了Go语言中container/list包实现的双向链表,并探讨了如何使用链表实现LRU缓存,LRU缓存通过维护一个双向... 目录力扣:146. LRU 缓存主要结构 List 和 Element常用方法1. 初始化链表2.

golang字符串匹配算法解读

《golang字符串匹配算法解读》文章介绍了字符串匹配算法的原理,特别是Knuth-Morris-Pratt(KMP)算法,该算法通过构建模式串的前缀表来减少匹配时的不必要的字符比较,从而提高效率,在... 目录简介KMP实现代码总结简介字符串匹配算法主要用于在一个较长的文本串中查找一个较短的字符串(称为

通俗易懂的Java常见限流算法具体实现

《通俗易懂的Java常见限流算法具体实现》:本文主要介绍Java常见限流算法具体实现的相关资料,包括漏桶算法、令牌桶算法、Nginx限流和Redis+Lua限流的实现原理和具体步骤,并比较了它们的... 目录一、漏桶算法1.漏桶算法的思想和原理2.具体实现二、令牌桶算法1.令牌桶算法流程:2.具体实现2.1

Python如何实现PDF隐私信息检测

《Python如何实现PDF隐私信息检测》随着越来越多的个人信息以电子形式存储和传输,确保这些信息的安全至关重要,本文将介绍如何使用Python检测PDF文件中的隐私信息,需要的可以参考下... 目录项目背景技术栈代码解析功能说明运行结php果在当今,数据隐私保护变得尤为重要。随着越来越多的个人信息以电子形

0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型的操作流程

《0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeekR1模型的操作流程》DeepSeekR1模型凭借其强大的自然语言处理能力,在未来具有广阔的应用前景,有望在多个领域发... 目录0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型,3步搞定一个应

windos server2022里的DFS配置的实现

《windosserver2022里的DFS配置的实现》DFS是WindowsServer操作系统提供的一种功能,用于在多台服务器上集中管理共享文件夹和文件的分布式存储解决方案,本文就来介绍一下wi... 目录什么是DFS?优势:应用场景:DFS配置步骤什么是DFS?DFS指的是分布式文件系统(Distr

SpringBoot使用Apache Tika检测敏感信息

《SpringBoot使用ApacheTika检测敏感信息》ApacheTika是一个功能强大的内容分析工具,它能够从多种文件格式中提取文本、元数据以及其他结构化信息,下面我们来看看如何使用Ap... 目录Tika 主要特性1. 多格式支持2. 自动文件类型检测3. 文本和元数据提取4. 支持 OCR(光学