数据结构——深度优先遍历(DFS)无向连通图

2023-10-30

以下是数据结构中关于深度优先遍历无向连通图的操作(编程风格参考严蔚敏版数据结构)。
其实深度优先遍历就是二叉树的先序遍历的推广。

头文件以及宏定义

#include<iostream>
#include<stdio.h>
using namespace std; 
typedef char VerTexType; 
typedef int ArcType;
#define MaxInt 32767
#define MVNum 100
#define OK 1
#define ERROR -1;
bool visited[MVNum];

说明: VerTexType; //代表节点变量的类型(一般我们用ABCD表示节点,所以用char) typedef int
ArcType; // 代表边变量的类型(肯定用长度表示边呀,所以用int或者double都可)
#define MaxInt 32767 //边的最大值(表示目标不可达)
#define MVNum 100 //最大节点数
bool visited[MVNum];//标记访问记录的数组;宏定义会自动赋值为0

无向图结构体的定义

typedef struct{
	VerTexType vexs[MVNum] {'A','B','C','D','E','F','G','H'};//节点表
	ArcType arcs[MVNum][MVNum];//邻接表(肯定是个正方形的矩阵)
	int vexnum = 8,arcnum = 9;//该邻接矩阵的节点数、边数  
}AMGraph; 

说明:
为了演示方便,就直接写死节点名称、节点数和边数了。有需要时自行修改即可。

创建无向图

status CreateUDN(AMGraph &G){//创建无向图 	
	for(int i=0;i<G.vexnum;i++){
		for(int j=0;j<G.vexnum;j++){
			if(i==j){
				G.arcs[i][j] = 0;//自己到自己的距离为0
			}else
				G.arcs[i][j] = MaxInt;//初始状态全部节点之间相互不可达
		}
	}
	G.arcs[0][1]=1;
	G.arcs[0][2]=1;
	G.arcs[1][3]=1;
	G.arcs[1][4]=1;
	G.arcs[2][5]=1;
	G.arcs[2][6]=1;
	G.arcs[3][7]=1;
	G.arcs[4][7]=1;
	G.arcs[5][6]=1;
	for(int i=0;i<G.vexnum;i++){
		for(int j=i+1;j<G.vexnum;j++){
			if(G.arcs[i][j]==1){
				G.arcs[j][i] = 1;
			} 
		}
	}//矩阵对称,生成矩阵下三角
	return OK; 
}

说明:
这个邻接表的设置(即节点之间边的设置)如下图所示:
在这里插入图片描述
对称矩阵的生成,只需要把下标i和j置换即可。

DFS(深度优先遍历)核心代码

void DFS(AMGraph &G,VerTexType v){//节点v 
	int vi = LocateVex(G,v);//v(v-index)的下标 
	cout<<G.vexs[vi]<<" ";//输出当前节点
	visited[vi] = true;//已访问 
	for(int vn=FirstAdjVex(G,v);vn>=0;vn=NextAdjVex(G,v,vn)){//vn(v-next)表示v的全部邻接点的下标(如果vn<0表示不存在邻接点) 
		if(!visited[vn]){//当前邻接点未被访问过,那就访问该节点
			VerTexType V = Transform(G,vn);//将该下标转回节点名称进行迭代 
			DFS(G,V);
		} 
	} 
} 

说明:
vi(v-index)表示v的下标
vn(v-next)表示v的全部邻接点的下标(如果vn<0表示不存在邻接点)
因为这里DFS第二个参数类型是节点而不是下标,所以获取邻接节点下标后要转回节点V的形式递归调用DFS;
FirstAdjVex(G,v)获取v的第一个邻接节点
NextAdjVex(G,v,vn)依次获取v的每个邻接节点的下标

获取节点下标

int LocateVex(AMGraph G, VerTexType v){
	int i;
	for(i=0;i<G.vexnum;i++){
		if(G.vexs[i]==v){
			return i;
		}
	} 
	return ERROR;
}

将下标转换成节点

VerTexType Transform(AMGraph G, int vn){
	return G.vexs[vn]; 
}

获取当前的第一个邻接节点以及全部邻接节点下标

int FirstAdjVex(AMGraph G,VerTexType v){//v的第一个邻接点 
	int vi = LocateVex(G,v);
	for(int i=0;i<G.vexnum;i++){
		if(!visited[i]&&G.arcs[vi][i]==1){
			return i;//找到邻接点且此邻接点未被访问过 
		}
	} 
	return ERROR;//未找到邻接点 
}

int NextAdjVex(AMGraph G,VerTexType v ,int vn){//v相对于vn的下一个邻接点 
	int vi = LocateVex(G,v);
	
	for(int i=vn+1;i<G.vexnum;i++){
		if(!visited[i]&&G.arcs[vi][i]==1){
			return i;//找到邻接点且此邻接点未被访问过 
		}
	} 
	return ERROR;//未找到下一个邻接点 
}

注意:获取下一个邻接节点不需要从0开始遍历,从上一个邻接节点的下标开始遍历即可。

输出邻接表

void ShowGraph(AMGraph G){
	cout<<" ";
	for(int i=0;i<G.vexnum;i++){
		cout<<" "<<G.vexs[i];
	}
	cout<<endl;
	for(int i=0;i<G.vexnum;i++){
		cout<<G.vexs[i]<<" ";
		for(int j=0;j<G.vexnum;j++){
			if(G.arcs[i][j]==MaxInt){
				cout<<"* ";
			}else{
				cout<<G.arcs[i][j]<<" ";
			}
				
		}
		cout<<endl;
	}
}

源码执行结果:

在这里插入图片描述

执行过程:
A的第一个邻接节点为B,此时A访问过了(输出A),去访问B
B的第一个邻接节点为A,但是A和B都访问过了(输出B),往右寻找:D节点未被访问过,去访问D;
D被访问(输出D),D的第一个邻接节点是B(已访问),向右寻找:寻到为被访问过的H;
访问H(输出H),H的第一个邻接节点为D(已访问),向右寻找:寻到E;
访问E(输出E),此时E的邻接点B和H都已访问。
还记得DFS里循环的这行代码吗?
for(int vn=FirstAdjVex(G,v);vn>=0;vn=NextAdjVex(G,v,vn))
刚才就是执行了vn=FirstAdjVex(G,v)这一次循环迭代DFS的过程,然后我们执行vn=NextAdjVex(G,v,vn)的循环迭代DFS的过程。
此时vn = 2(也就是对应C),C未被访问过,访问C;
访问C(输出C),C的第一个邻接节点是A(已访问),向右寻找:寻找到F未被访问过,访问F;
访问F(输出F),F的第一个邻接节点是C(已访问),向右寻找:寻找到G未被访问过,访问G。
访问G(输出G),此时G的邻接节点C和F全部都被访问过了,本次循环结束。
接下来的循环还在进行,但是全部节点都已经被访问过了,就不会输出了,直到嵌套的循环全部跑完,程序结束。

换个起始点一样适用

在这里插入图片描述
在这里插入图片描述

完整源代码

#include<iostream>
#include<stdio.h>
using namespace std; 
typedef char VerTexType; //代表节点变量的类型(一般我们用ABCD表示节点,所以用char)
typedef int ArcType; //  代表边变量的类型(肯定用长度表示边呀,所以用int或者double都可)
#define MaxInt 32767 //边的最大值(表示目标不可达)
#define MVNum 100    //最大节点数 
#define OK 1
#define ERROR -1;
typedef int status;
bool visited[MVNum];//标记访问记录的数组;宏定义会自动赋值为0 
typedef struct{
	VerTexType vexs[MVNum] {'A','B','C','D','E','F','G','H'};//节点表
	ArcType arcs[MVNum][MVNum];//邻接表(肯定是个正方形的矩阵)
	int vexnum = 8,arcnum = 9;//该邻接矩阵的节点数、边数  
}AMGraph; 

int LocateVex(AMGraph G, VerTexType v){
	int i;
	for(i=0;i<G.vexnum;i++){
		if(G.vexs[i]==v){
			return i;
		}
	} 
	return ERROR;
}

status CreateUDN(AMGraph &G){//创建无向图 	
	for(int i=0;i<G.vexnum;i++){
		for(int j=0;j<G.vexnum;j++){
			if(i==j){
				G.arcs[i][j] = 0;
			}else
				G.arcs[i][j] = MaxInt;//初始状态全部节点之间相互不可达
		}
	}
	G.arcs[0][1]=1;
	G.arcs[0][2]=1;
	G.arcs[1][3]=1;
	G.arcs[1][4]=1;
	G.arcs[2][5]=1;
	G.arcs[2][6]=1;
	G.arcs[3][7]=1;
	G.arcs[4][7]=1;
	G.arcs[5][6]=1;
	for(int i=0;i<G.vexnum;i++){
		for(int j=i+1;j<G.vexnum;j++){
			if(G.arcs[i][j]==1){
				G.arcs[j][i] = 1;
			} 
		}
	}//矩阵对称 
	return OK; 
}

void ShowGraph(AMGraph G){
	cout<<" ";
	for(int i=0;i<G.vexnum;i++){
		cout<<" "<<G.vexs[i];
	}
	cout<<endl;
	for(int i=0;i<G.vexnum;i++){
		cout<<G.vexs[i]<<" ";
		for(int j=0;j<G.vexnum;j++){
			if(G.arcs[i][j]==MaxInt){
				cout<<"* ";
			}else{
				cout<<G.arcs[i][j]<<" ";
			}
				
		}
		cout<<endl;
	}
}

VerTexType Transform(AMGraph G, int vn){
	return G.vexs[vn]; 
}

int FirstAdjVex(AMGraph G,VerTexType v){//v的第一个邻接点 
	int vi = LocateVex(G,v);
	for(int i=0;i<G.vexnum;i++){
		if(!visited[i]&&G.arcs[vi][i]==1){
			return i;//找到邻接点且此邻接点未被访问过 
		}
	} 
	return ERROR;//未找到邻接点 
}

int NextAdjVex(AMGraph G,VerTexType v ,int vn){//v相对于vn的下一个邻接点 
	int vi = LocateVex(G,v);
	
	for(int i=vn+1;i<G.vexnum;i++){
		if(!visited[i]&&G.arcs[vi][i]==1){
			return i;//找到邻接点且此邻接点未被访问过 
		}
	} 
	return ERROR;//未找到下一个邻接点 
}

void DFS(AMGraph &G,VerTexType v){//节点v 
	int vi = LocateVex(G,v);//vi(v-index)的下标 
	cout<<G.vexs[vi]<<" ";//输出当前节点
	visited[vi] = true;//已访问 
	for(int vn=FirstAdjVex(G,v);vn>=0;vn=NextAdjVex(G,v,vn)){//vn(v-next)表示v的全部邻接点的下标(如果vn<0表示不存在邻接点) 
//		cout<<vn<<"\n"; 
		if(!visited[vn]){//当前邻接点未被访问过 
			VerTexType V = Transform(G,vn);//将该下标转回节点名称进行迭代 
			DFS(G,V);
		} 
	} 
} 

int main(){
	AMGraph G;
	CreateUDN(G);
	ShowGraph(G);
	VerTexType V;
	cout<<"\n请输入开始遍历的节点:";
	cin>>V;
	DFS(G,V);
	return 0;
}

敬请批评指正!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

数据结构——深度优先遍历(DFS)无向连通图 的相关文章

随机推荐

  • 《C++11标准库》4.2头文件( Header File)

    在C 标准化过程中 将C 标准库中所有的标识符都定义于 namespace std 内 这样的作法不具备向后兼容性 因为原先的C C 头文件都将C 标准库的标识符定义于全局范围 标准化过程中有些 class 接口也有了变动 为此 C sta
  • vscode 变量命名插件_10款VS Code插件神器,第7款超级实用!

    VS Code是这两年非常热门的一款开发工具 它不仅有提升开发体验的界面 轻量化的编辑器 还有丰富而强大的插件 这些优秀的插件使得VS Code生态体系更加吸引人 让开发效率大大提升 本文来介绍10款高效的VS Code插件 总有一款能够惊
  • iOS 使用“./compile-ffmpeg.sh all”编译 ijkplayer 报错“C compiler test failed.

    原因 compile ffmpeg sh 脚本找不到 Xcode 解决方案 compile ffmpeg sh clean sudo usr bin xcode select switch Applications Xcode app Co
  • 微信小程序相关面试题

    微信小程序相关面试题 1 请谈谈wxml与标准的html的异同 2 请谈谈WXSS和CSS的异同 3 请谈谈微信小程序主要目录和文件的作用 4 请谈谈小程序的双向绑定和vue的异同 5 简单描述下微信小程序的相关文件类型 6 微信小程序有哪
  • 未竟的Web 3.0理想,DID或打开关键入口

    寄托往往意味着断送 维克多 雨果 悲惨世界 里的经典之言正是Web2 0时代的写照 近期 B站 答题领卡兑换大会员 活动被网友指出涉嫌出卖用户个人隐私 虽然B站回应称 该页面系文案措辞不妥引起误会 目前已下线该页面并整改 但风波并没有止息
  • 未预期的符号 `then’ 附近有语法错误加粗样式

    未预期的符号 then 附近有语法错误加粗样式 编写shell脚本执行时发生如下报错 后经分析 错误原因是因为if后面没有加空格 加入空格之后则不再存在语法错误 修改后脚本截图
  • 重启人生1.0-day1:704. 二分查找;27. 移除元素

    数组理论基础 704 二分查找 左闭右闭区间 left right size len nums left 0 right size 1 while left lt right 当left right的时候 循环区间是个合法区间 middle
  • dataguard-(ORA-16004/ORA-01196/ORA-01110)

    author skatetime 2009 08 01 1 故障现象 一次突然断电导致我的standby open时报如下的错误 ORA 16004 备份数据库需要恢复ORA 01196 文件 1 由于介质恢复会话失败而不一致ORA 011
  • Unity Text 透明

    Unity Text透明化问题 Shader UI TextBlend Properties HideInInspector MainTex Texture 2D white HideInInspector BlendTex Blend T
  • 集度汽车(武汉java)一面

    hashMap底层结构 hash算法的好处是什么 为什么采用数组加链表 数组有哪些特性 内存地址连续 查找快 怎么解决哈希碰撞 链地址法 并发编程需要注意哪些地方 如何处理变量的线程安全 sycronized关键字原理 分布式锁实现方式 有
  • 华子这题确实不错!

    我们来看一下这道题目到底哪里不错 题目描述 小王在进行游戏大闯关 有一个关卡需要输入一个密码才能通过 密码获得的条件如下 在一个密码本中 每一页都有一个由 26 个小写字母组成的若干位密码 从它的末尾开始依次去掉一位得到的新密码也在密码本中
  • 小程序从后台切到前台首页刷新机制 (Banner图 )

    问题 后台 banner 图 更新后 小程序首页不会自动更新 注明 这里只针对首页 其他页进入 onload即可 解决方案一 直接在page页面 每次onShow 都执行 解决方案二 app js 文件 app js App onLaunc
  • MongoDB报错:org.springframework.data.mongodb.UncategorizedMongoDbException: Exception authenticating

    org springframework data mongodb UncategorizedMongoDbException Exception authenticating MongoCredential Caused by com mo
  • Me and My Girlfriend靶机实验

    目录 靶机描述 准备 信息收集 一键三连IP 端口 目录扫描 ip 端口扫描 目录扫描 需要用到kali中的dirsearch 漏洞分析 渗透攻击 提权 这里我们可以尝试使用漏洞检查脚本 靶机的下载地址 Me and My Girlfrie
  • Ubuntu 18.04 镜像下载

    打开 官网 点击 下载 点击 Ubuntu桌面系统 点击 其他下载 鼠标滑到最下方 点击 Ubuntu 18 04 6 桌面版 64位 点击 保存
  • STM32 USB学习笔记9

    主机环境 Windows 7 SP1 开发环境 MDK5 14 目标板 STM32F103C8T6 开发库 STM32F1Cube库和STM32 USB Device Library 现在我们来分析VCP例程的最后一个文件USB设备类的us
  • LDAP概念和原理

    http blog sina com cn s blog 6151984a0100ey3z html 什么是目录服务 目录服务就是按照 树状存储信息的模式 目录服务的特点 目录服务与关系型数据库不同 目录服务的数据类型主要是字符型 而不是关
  • 如何通过redis 配置提高redis的性能

    redis的性能 我拿什么拯救 持久化选项 客户端输出缓冲限制 数据结构优化 压缩列表 网络配置 连接池 客户端输出缓冲限制 数据结构优化 压缩列表 网络配置 连接池 不冷战 不任性 多沟通 用舒服的方式喜欢和爱 知道承担 懂得分享 一起进
  • 组件扫描功能

    Spring提供了注解扫描 利用组件扫描注解和组件注解配合 可以自动扫描包空间自动创建Bean对象 减少编码 提高效率 配置文件 Configuration ComponentScan basePackages cn tedu Demo p
  • 数据结构——深度优先遍历(DFS)无向连通图

    以下是数据结构中关于深度优先遍历无向连通图的操作 编程风格参考严蔚敏版数据结构 其实深度优先遍历就是二叉树的先序遍历的推广 头文件以及宏定义 include