图的遍历(详解DFS与BFS)

2023-11-05

首先,我们来看一下涉及的知识点:

:图 G=(V,E) 由顶点集 V 和边集 E 组成。每条边对应一个点对 (v,w),其中 v,w 属于 V 。如果图中的点对是有序的,那么该图就是有向图,反之为无向图

邻接点:若顶点 v 与 w 之间存在一条边,则认为顶点 v 与 w 邻接。

:图中的每条边都可以对应一个数值,这种与边相关的数值称为权。

路径:在图 G 中,顶点 v1 到 vk 的路径是一个顶点序列 v1,v2,···,vk。

连通图:在无向图 G 中,若两个顶点之间存在路径,则认为这两个顶点是连通的。如果在无向图 G 中,任意两个顶点都是连通的,则称 G 是连通图。

完全图:若图中任意两个顶点之间都存在一条边,则该图为完全图。

稀疏图和稠密图:当图中边的数量比较少时,称该图为稀疏图;而当图接近完全图时,称该图为稠密图

接下来,我们进入正题:

1、深度优先遍历(DFS)

深度优先遍历类似于树的先序遍历。具体方法描述如下:

(1)从起始顶点 v 出发,首先访问顶点 v;
(2)选择一个与顶点 v 相邻接且没有被访问过的顶点 w 作为新的起始点,继续深度优先遍历,直到顶点 v 的所有邻接点都被访问过。

另外,若图不是连通图,则一次深度优先遍历只能访问到起始点所在连通分量中的所有顶点,而访问不到其它顶点。因此要从其它连通分量中选择起始点,继续深度优先遍历,才能将图中的所有顶点都访问一遍。为了保证在遍历过程中每个顶点只会被访问一次,我们借助一个辅助数组 Visit[] 来做一下标记。若 Visit[] 为1,则说明顶点 vi 已被访问过,若为0,则没有被访问过。

邻接矩阵的DFS代码:

# include <iostream>
# include <iomanip>
# define maxn 20
using namespace std;
typedef struct VexType {  //顶点类型
	int code;             //顶点编号
	char data;            //顶点信息
}VexType;
typedef struct Graph {     //邻接矩阵类型
	int arcs[maxn][maxn];  //邻接矩阵
	int vexnum, arcnum;    //顶点数和边的个数
	VexType vexs[maxn];    //顶点信息
	int type;              //邻接矩阵的类型(1.无向图 2.有向图)
}Graph;

int Visit[maxn] = { 0 };   //辅助数组,标记点是否已被访问过

void Create_Graph(Graph& G)   //创建
{
	cout << "请输入图的类型(1.无向图 2.有向图):";
	cin >> G.type;
	cout << "顶点的个数:";
	cin >> G.vexnum;
	cout << "请输入各顶点的名称:";
	for (int i = 0; i < G.vexnum; i++) {
		cin >> G.vexs[i].data;
		G.vexs[i].code = i;  //按顺序为点编号
	}
	for (int i = 0; i < G.vexnum; i++) {
		for (int j = 0; j < G.vexnum; j++) {
			G.arcs[i][j] = 0;  //邻接矩阵初始化,将所有元素的初始值设为0 
		}
	}
	cout << "请输入边的个数:";
	cin >> G.arcnum;
	cout << "请输入各边起点和终点的编号及权重:" << endl;
	int x, y, w;  //x:起始点,y:终点,w:权重
	for (int i = 0; i < G.arcnum; i++) {
		cin >> x >> y >> w;
		if (G.type == 1) {  //如果是无向图,对称边也要赋值为权重
			G.arcs[x][y] = w;
			G.arcs[y][x] = w;
		}
		else {
			G.arcs[x][y] = w;
		}
	}
}
  

void DFS(Graph G, int n)
{
	Visit[n] = 1;            //将起始点标记为已被访问过的状态
	cout << G.vexs[n].data;  //输出起始点的信息
	for (int i = 0; i < G.vexnum; i++) {
		//遍历起始点的所有邻接点,若邻接点已被访问过,则p继续向后遍历
		//否则,以当前邻接点为新的起始点递归进行深度优先遍历
		if (!Visit[i] && (G.arcs[n][i] > 0 || G.arcs[i][n] > 0)) {
			DFS(G, i);
		}
	}
}

void DFS_Graph(Graph G)  //深度优先遍历,对每个顶点访问且仅访问一次
{
	for (int i = 0; i < G.vexnum; i++) {
		Visit[i] = 0;  //辅助数组初始化
	}
	for (int i = 0; i < G.vexnum; i++) {
		if (!Visit[i]) {  //对每一个未被访问过的顶点均调用一次深度优先遍历
			DFS(G, i);
		}
	}
}


int main()
{
	Graph G;
	Create_Graph(G);     //创建
	cout << "DFS:";
	DFS_Graph(G);        //深度优先遍历,对每个顶点访问且仅访问一次
	cout << endl;
	return 0;
}

邻接表的DFS代码:

# include <iostream>
# include <queue>
# define SIZE 20
# define NEWSIZE 20
using namespace std;
typedef struct ArcNode {  //边的结点结构类型
	int adjvex;           //该边的终点编号
	int weight;           //该边的权值
	struct ArcNode* nextarc;  //指向下一条边的指针
}ArcNode;
typedef struct VexNode {  //顶点结构
	char data;
	ArcNode* firstarc;    //指向第一条与该顶点有关的边的指针
}VexNode;
typedef struct Graph {    //邻接表结构类型
	VexNode* VNode;       //定义邻接表
	int vexnum, arcnum;   //顶点数和边的个数
	int type;             //图的种类
	int size;             //邻接表的大小
}Graph;

int* Visit; //辅助数组,标记点是否已被访问过

void Create_Graph(Graph& G)   //创建
{
	cout << "请输入图的类型(1.无向图 2.有向图):";
	cin >> G.type;
	cout << "顶点的个数:";
	cin >> G.vexnum;
	G.VNode = (VexNode*)malloc(SIZE * sizeof(VexNode));
	G.size = SIZE;
	while (G.size < G.vexnum) {   //根据点的个数动态分配空间
		G.VNode = (VexNode*)realloc(G.VNode, (G.size + NEWSIZE) * sizeof(VexNode));
		G.size += NEWSIZE;
	}
	Visit = (int*)malloc((G.size + 10) * sizeof(int));
	cout << "请输入各顶点的名称:";
	for (int i = 0; i < G.vexnum; i++) {
		cin >> G.VNode[i].data;
		G.VNode[i].firstarc = NULL;  //邻接表初始化,所有单向链表均为空表
	}
	cout << "请输入边的个数:";
	cin >> G.arcnum;
	cout << "请输入各边起点和终点的编号及权重:" << endl;
	int x, y, w;    //x:起始点,y:终点,w:权重
	ArcNode* p, * q;
	for (int i = 0; i < G.arcnum; i++) {
		cin >> x >> y >> w;
		p = (ArcNode*)malloc(sizeof(ArcNode)); //创建一个用于存放当前边的结点p
		p->nextarc = NULL;
		p->adjvex = y;
		p->weight = w;
		q = G.VNode[x].firstarc;
		//将边按顺序插入到链表末尾
		if (q == NULL) {   
			G.VNode[x].firstarc = p;
		}
		else {
			while (q->nextarc != NULL) { 
				q = q->nextarc;
			}
			q->nextarc = p;
		}
		if (G.type == 1) {    //如果是无向图,要再创建一个表示对称边的结点p
			p = (ArcNode*)malloc(sizeof(ArcNode));
			p->nextarc = NULL;
			p->adjvex = x;
			p->weight = w;
			q = G.VNode[y].firstarc;
			if (q == NULL) {
				G.VNode[y].firstarc = p;
			}
			else {
				while (q->nextarc != NULL) {
					q = q->nextarc;
				}
				q->nextarc = p;
			}
		}
	}
}

void DFS(Graph G, int n)
{
	Visit[n] = 1;             //将起始点标记为已被访问过的状态
	cout << G.VNode[n].data;  //输出起始点的信息
	ArcNode* p = G.VNode[n].firstarc;
	while (p) {
		//遍历起始点的所有邻接点,若邻接点已被访问过,则p继续向后遍历
		//否则,以当前邻接点为新的起始点递归进行深度优先遍历
		if (!Visit[p->adjvex]) {  
			DFS(G, p->adjvex);
		}
		p = p->nextarc;
	}
}

void DFS_Graph(Graph G)  //深度优先遍历,对每个顶点访问且仅访问一次
{
	for (int i = 0; i < G.vexnum; i++) {
		Visit[i] = 0;  //辅助数组初始化
	}
	for (int i = 0; i < G.vexnum; i++) {
		if (!Visit[i]) {  //对每一个未被访问过的顶点均调用一次深度优先遍历
			DFS(G, i);
		}
	}
}


int main()
{
	Graph G;
	Create_Graph(G);     //创建
	cout << "DFS:";
	DFS_Graph(G);        //深度优先遍历,对每个顶点访问且仅访问一次
	cout << endl;
	return 0;
}

 运行结果:

请输入图的类型(1.无向图 2.有向图):1
顶点的个数:8
请输入各顶点的名称:A B C D E F G H
请输入边的个数:9
请输入各边起点和终点的编号及权重:
0 1 3
0 2 12
1 3 5
1 4 6
2 3 7
2 4 10
5 6 9
5 7 4
6 7 13
DFS:ABDCEFGH

对邻接矩阵DFS的时间复杂度为 O(n^2),对邻接表DFS的时间复杂度为 O(n+e)( n 是图中顶点的个数,e 是边的个数),但邻接表的创建要较为复杂。

2、广度优先遍历(BFS)

广度优先遍历类似于树的层次遍历。具体方法描述如下:

(1)从起始顶点 v 出发,首先访问顶点 v;

(2)依次访问 v 的所有没有被访问过的邻接点 w1,w2,···,wk;

(3)按照 w1,w2,···,wk 的次序,访问它们各自所有未被访问过的邻接点;

(4)以此类推,直到图中所有与起始点 v 连通的顶点都被访问过为止。

同样,若图不是连通图,则一次广度优先遍历只能访问到与起始点连通的那些顶点,而访问不到其它顶点。因此要从其它连通分量中选择起始点,继续广度优先遍历,才能将图中的所有顶点都访问一遍。为了保证在遍历过程中每个顶点只会被访问一次,我们也要借助一个辅助数组 Visit[] 来做一下标记。若 Visit[] 为1,则说明顶点 vi 已被访问过,若为0,则没有被访问过。

 

邻接矩阵的BFS代码:

# include <iostream>
# include <iomanip>
# include <queue>
# define maxn 20
using namespace std;
typedef struct VexType {  //顶点类型
	int code;             //顶点编号
	char data;            //顶点信息
}VexType;
typedef struct Graph {     //邻接矩阵类型
	int arcs[maxn][maxn];  //邻接矩阵
	int vexnum, arcnum;    //顶点数和边的个数
	VexType vexs[maxn];    //顶点信息
	int type;              //邻接矩阵的类型(1.无向图 2.有向图)
}Graph;

int Visit[maxn] = { 0 };   //辅助数组,标记点是否已被访问过

void Create_Graph(Graph& G)   //创建
{
	cout << "请输入图的类型(1.无向图 2.有向图):";
	cin >> G.type;
	cout << "顶点的个数:";
	cin >> G.vexnum;
	cout << "请输入各顶点的名称:";
	for (int i = 0; i < G.vexnum; i++) {
		cin >> G.vexs[i].data;
		G.vexs[i].code = i;  //按顺序为点编号
	}
	for (int i = 0; i < G.vexnum; i++) {
		for (int j = 0; j < G.vexnum; j++) {
			G.arcs[i][j] = 0;  //邻接矩阵初始化,将所有元素的初始值设为0 
		}
	}
	cout << "请输入边的个数:";
	cin >> G.arcnum;
	cout << "请输入各边起点和终点的编号及权重:" << endl;
	int x, y, w;  //x:起始点,y:终点,w:权重
	for (int i = 0; i < G.arcnum; i++) {
		cin >> x >> y >> w;
		if (G.type == 1) {  //如果是无向图,对称边也要赋值为权重
			G.arcs[x][y] = w;
			G.arcs[y][x] = w;
		}
		else {
			G.arcs[x][y] = w;
		}
	}
}


void BFS(Graph G, int n)
{
	queue<int>Q;
	Visit[n] = 1;            //将起始点标记为已被访问过的状态
	cout << G.vexs[n].data;  //先输出起始点信息
	Q.push(n);               //起始点入队
	while (!Q.empty()) {     
		n = Q.front();       //队头元素出队
		Q.pop();
		for (int i = 0; i < G.vexnum; i++) {
			//遍历当前队头结点的所有邻接点,若邻接点已被访问过,则p继续向后遍历
			//否则入队,并标记为已访问状态,防止重复入队
			if (!Visit[i] && (G.arcs[n][i] > 0 || G.arcs[i][n] > 0)) {
				Visit[i] = 1;
				cout << G.vexs[i].data;
				Q.push(i);
			}
		}
	}
}

void BFS_Graph(Graph G)  //广度优先遍历,对每个顶点访问且仅访问一次
{
	for (int i = 0; i < G.vexnum; i++) {
		Visit[i] = 0;   //辅助数组初始化
	}
	for (int i = 0; i < G.vexnum; i++) {
		if (!Visit[i]) {  //对每一个未被访问过的顶点均调用一次广度优先遍历
			BFS(G, i);
		}
	}
}


int main()
{
	Graph G;
	Create_Graph(G);     //创建
	cout << "BFS:";
	BFS_Graph(G);        //广度优先遍历,对每个顶点访问且仅访问一次
    cout<<endl;
	return 0;
}

 

邻接表的BFS代码:

# include <iostream>
# include <queue>
# define SIZE 20
# define NEWSIZE 20
using namespace std;
typedef struct ArcNode {  //边的结点结构类型
	int adjvex;           //该边的终点编号
	int weight;           //该边的权值
	struct ArcNode* nextarc;  //指向下一条边的指针
}ArcNode;
typedef struct VexNode {  //顶点结构
	char data;
	ArcNode* firstarc;    //指向第一条与该顶点有关的边的指针
}VexNode;
typedef struct Graph {    //邻接表结构类型
	VexNode* VNode;       //定义邻接表
	int vexnum, arcnum;   //顶点数和边的个数
	int type;             //图的种类
	int size;             //邻接表的大小
}Graph;

int* Visit; //辅助数组,标记点是否已被访问过

void Create_Graph(Graph& G)   //创建
{
	cout << "请输入图的类型(1.无向图 2.有向图):";
	cin >> G.type;
	cout << "顶点的个数:";
	cin >> G.vexnum;
	G.VNode = (VexNode*)malloc(SIZE * sizeof(VexNode));
	G.size = SIZE;
	while (G.size < G.vexnum) {   //根据点的个数动态分配空间
		G.VNode = (VexNode*)realloc(G.VNode, (G.size + NEWSIZE) * sizeof(VexNode));
		G.size += NEWSIZE;
	}
	Visit = (int*)malloc((G.size + 10) * sizeof(int));
	cout << "请输入各顶点的名称:";
	for (int i = 0; i < G.vexnum; i++) {
		cin >> G.VNode[i].data;
		G.VNode[i].firstarc = NULL;  //邻接表初始化,所有单向链表均为空表
	}
	cout << "请输入边的个数:";
	cin >> G.arcnum;
	cout << "请输入各边起点和终点的编号及权重:" << endl;
	int x, y, w;    //x:起始点,y:终点,w:权重
	ArcNode* p, * q;
	for (int i = 0; i < G.arcnum; i++) {
		cin >> x >> y >> w;
		p = (ArcNode*)malloc(sizeof(ArcNode)); //创建一个用于存放当前边的结点p
		p->nextarc = NULL;
		p->adjvex = y;
		p->weight = w;
		q = G.VNode[x].firstarc;
		//将边按顺序插入到链表末尾
		if (q == NULL) {   
			G.VNode[x].firstarc = p;
		}
		else {
			while (q->nextarc != NULL) { 
				q = q->nextarc;
			}
			q->nextarc = p;
		}
		if (G.type == 1) {    //如果是无向图,要再创建一个表示对称边的结点p
			p = (ArcNode*)malloc(sizeof(ArcNode));
			p->nextarc = NULL;
			p->adjvex = x;
			p->weight = w;
			q = G.VNode[y].firstarc;
			if (q == NULL) {
				G.VNode[y].firstarc = p;
			}
			else {
				while (q->nextarc != NULL) {
					q = q->nextarc;
				}
				q->nextarc = p;
			}
		}
	}
}


void BFS(Graph G, int n)
{
	queue<int>Q;
	Visit[n] = 1;             //将起始点标记为已被访问过的状态
	cout << G.VNode[n].data;  //先输出起始点信息
	Q.push(n);                //起始点入队
	while (!Q.empty()) {
		n = Q.front();        //队头元素出队
		Q.pop();
		ArcNode* p = G.VNode[n].firstarc;
		while (p) {
			//遍历当前队头结点的所有邻接点,若邻接点已被访问过,则p继续向后遍历
			//否则入队,并标记为已访问状态,防止重复入队
			if (!Visit[p->adjvex]) {
				Visit[p->adjvex] = 1;
				cout << G.VNode[p->adjvex].data;
				Q.push(p->adjvex);
			}
			p = p->nextarc;
		}
	}
}

void BFS_Graph(Graph G)  //广度优先遍历,对每个顶点访问且仅访问一次
{
	for (int i = 0; i < G.vexnum; i++) {
		Visit[i] = 0;   //辅助数组初始化
	}
	for (int i = 0; i < G.vexnum; i++) {
		if (!Visit[i]) {  //对每一个未被访问过的顶点均调用一次广度优先遍历
			BFS(G, i);
		}
	}
}


int main()
{
	Graph G;
	Create_Graph(G);     //创建
	cout << "BFS:";
	BFS_Graph(G);        //广度优先遍历,对每个顶点访问且仅访问一次
	cout << endl;
	return 0;
}

运行结果:

请输入图的类型(1.无向图 2.有向图):1
顶点的个数:8
请输入各顶点的名称:A B C D E F G H
请输入边的个数:9
请输入各边起点和终点的编号及权重:
0 1 3
0 2 12
1 3 5
1 4 6
2 3 7
2 4 10
5 6 9
5 7 4
6 7 13
BFS:ABCDEFGH

相似的,对邻接矩阵BFS的时间复杂度为 O(n^2),对邻接表BFS的时间复杂度为 O(n+e)( n 是图中顶点的个数,e 是边的个数)。

以上就是我这次的全部学习成果,很高兴能与大家分享。

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

图的遍历(详解DFS与BFS) 的相关文章

  • tcp参数详解之tcp_fin_timeout

    tcp fin timeout INTEGER 默认值是 60 对于本端断开的socket连接 TCP保持在FIN WAIT 2状态的时间 对方可能会断开连接或一直不结束连接或不可预料的进程死亡 默认值为 60 秒 过去在2 2版本的内核中
  • 【Java-----IO流(一)之字节流详解】

    IO流概述和分类 IO流概述 IO 输入 输出 Input Output 流 是一种抽象概念 对数据传输的总称 也就是说数据在设备间的传输成为流 流的本质是数据传输 IO流 用来处理设备间数据传输问题的 常见的应用如 文件复制 文件上传 文
  • HTML+CSS+JS+node.js实现websocket聊天室

    本文实现如题所说 使用的websocket库是nodejs websocket库 可在网上直接下载安装 npm install nodejs websocket 使用是直接在文件中require即可 一开始想用PHP写后台实现服务器端web

随机推荐

  • 泊松分布近似正态分布的表达式_泊松分布的意义

    刚学的时候 脑子乱成浆糊 现在回过头来思考 总算有些澄清了 以下心得 主要参考了马同学的包子铺解答 以及 生物统计学基础 孙尚拱中文译版 泊松分布源于二项分布 而二项分布属于离散概率分布 二项分布 描述的是试验成功次数的概率分布 成功次数是
  • 使用两个队列实现一个栈

    问题分析 观察队列和栈的特点 队列是先进先出的 而栈是先进后出的 也就是说 要使用队列实现一个栈 就是利用队列实现先进后出的规律 当pop元素时 最后一个pop的元素为栈的栈顶元素 问题解决 利用两个队列实现一个栈 当push元素时 pus
  • android开发(36) Android WebView背景设置为透明

    xml布局
  • 震惊!中国地震台网数据爬取

    import scrapy import re from scrapy import Request from urllib import parse from SpiderDemo items import SpiderdemoItem
  • 组报文时,在char数组中插入0x00的方法

    在char数组中插入0x00 方法一 方法一对于短的组码 简单易实现 对于长的组码 稍有不慎就会数错字节数 还很难查出哪个字节出问题 方法二 对于短的组码没必要使用方法二 对于长组码 方便组码且不会数错字节数 代码最终实现 char buf
  • c++区块链实例_cpp 区块链模拟示例(五) 序列化

    有了区块和区块链的基本结构 有了工作量证明 我们已经可以开始挖矿了 剩下就是最核心的功能 交易 但是在开始实现交易这一重大功能之前 我们还要预先做一些铺垫 比如数据的序列化和启动命令解析 根据 用 Go 构建一个区块链 的目录 本章节的区块
  • 成功解决如何将csv文件转为utf8编码格式操作的方法

    成功解决如何将csv文件转为utf8编码格式操作的方法 目录 关于UTF 8 解决问题 解决方法 关于UTF 8 UTF 8 8位元 Universal Character Set Unicode Transformation Format
  • 编程实现Ctrl+A或V==是否被按下

    private void lvBookmarks KeyPress object sender KeyPressEventArgs e if e Control e KeyCode Keys A region 全选ListView控件lvB
  • 修复和预防Bug的成本的量化对比

    当我们打算提高软件质量的时候 首先考虑到的可能就是购买新工具的成本和实施新工具的人力成本 以及可能会因为增加了新的测试过程而 延长 的开发生命周期 但实际上 首先我们应该考虑从现在的产品生命周期中查找和修复问题产生的成本 除了这些直接的成本
  • 天才的引导历程

    这本书与科学15讲差不多 是数学科普类书籍 这里面对于无穷小数的思考 提出 可以看一看 学习数学的人真的可以看一看 2014 1 1
  • 前言技术:swagger

    1 前后端分离的特点 前后端分离是的前端与后端之间的职责更加明确 后台 负责业务处理 前端 负责显示逻辑 在这种情况下 前端和后端可以分别交付给专业的开发人员去做 所以是必须要定义前后端直接的对接 接口 否则各自为是则项目无法集成 这时就需
  • 基于51单片机的超声波水位液位监测仿真程序设计

    硬件设计 上一篇咱们说了基于液位传感器的优缺点 其中缺点就是测量距离有限 这里就引入了超声波的测距方式 该方式测量距离就大大增加 超声波测距系统原理 在超声探测电路中 发射端得到输出脉冲为一系列方波 其宽度为发射超声的时间间隔 被测物距离越
  • 中国移动OneOS助力全国大学生物联网竞赛开幕

    本文分享自中移物联网微信公众号 中国移动OneOS助力全国大学生物联网竞赛开幕 近日 2022年全国大学生物联网设计竞赛正式开赛 该项赛事是教育部高等学校计算机类专业教学指导委员会创办的物联网领域的学科竞赛 是以学科竞赛推动专业建设 培养大
  • Vmware虚拟机下三种网络模式配置

    原创链接 http blog csdn net collection4u article details 14127671 Vmware虚拟机下三种网络模式配置 VMware虚拟机有三种网络模式 分别是Bridged 桥接模式 NAT 网络
  • docker-compose部署Nacos集群

    docker compose部署Nacos集群 1 前置准备 docker nacos的数据库 2 创建nacos目录 3 切换到nacos目录下 创建并写nginx conf配置文件 4 创建并写docker compose yaml配置
  • Channel-wise Knowledge Distillation for Dense Prediction(ICCV 2021)原理与代码解析

    paper Channel wise Knowledge Distillation for Dense Prediction official implementation https github com irfanICMLL Torch
  • Vue3的从入门到实战的培训教程大纲

    Vue3的从入门到实战的培训教程大纲 第一部分 Vue3入门 Vue框架概述 介绍Vue的历史和特点 解释Vue的MVVM架构 Vue3的新特性 对比Vue2和Vue3的主要差异 强调Vue3的性能改进和优化 安装与配置Vue3 下载和安装
  • java 多线程-03-等待wait 和 通知 notify

    等待wait 和 通知 notify 引入 java多线程协作支持 wait notify是object类 任何对象都可以调用这两个方法 public final void wait throws InterruptedException
  • 如何使用宝塔部署网站

    1 根据自己的版本输入不同安装宝塔的命令 我用的使用的是finashell软件 安装及使用前一篇已经介绍过了 用的是第一个安装命令 yum install y wget wget O install sh https download bt
  • 图的遍历(详解DFS与BFS)

    首先 我们来看一下涉及的知识点 图 图 G V E 由顶点集 V 和边集 E 组成 每条边对应一个点对 v w 其中 v w 属于 V 如果图中的点对是有序的 那么该图就是有向图 反之为无向图 邻接点 若顶点 v 与 w 之间存在一条边 则