图的深度/广度优先搜索C++实现(基于邻接矩阵存储结构)
前言
关于数据结构中图的 深度/广度 优先搜索(基于邻接矩阵存储结构)
一、图
图的定义
图(graph)是 n (n>=0) 个元素的有限集。图可以表示成二元组的形式,即:Graph= ( V , E ) 。其中,V是图中数据元素的集合,通常称为顶点集;E是数据元素之间关系的集合,通常称为边集。数据元素用顶点表示,数据元素之间的关系用边(无方向)或弧(有方向)表示。
在这里定义一个无向图G如下,方便后续推导:
V={A,B,C,D,E}
E={(A,B),(A,C),(B,D),(B,E),(C,E)}
图的存储结构(邻接矩阵)
图的邻接矩阵 (adjacent matrix) 表示法是用一个一维数组来存放顶点的信息,再用一个二维数组来存放边或弧的信息的表示方法,又称数组表示法。常采用矩阵的形式来描述边或弧的信息。
例 (G的邻接矩阵表示):
| 0 | 1 | 1 | 0 | 0 |
| 1 | 0 | 0 | 1 | 1 |
| 1 | 0 | 0 | 0 | 1 |
| 0 | 1 | 0 | 0 | 0 |
| 0 | 1 | 1 | 0 | 0 |
邻接矩阵类的定义如下(C++):
class _adjMatrix /*邻接矩阵存储结构类名*/
{
public:
_adjMatrix() {};
~_adjMatrix() {};
int vexnum, edgenum, kind; /*顶点数、边(或弧)数及图的种类*/
vector<char> vex; /*存放顶点信息的一维数组*/
vector<vector<int> >edge; /*存边(或弧)的二维数组*/
//void Creadjm();/*简单结构建立图的邻接矩阵*/
//void list();/*输出函数*/
//int Dfs(int now);/*depth-first search,深度优先搜索*/
//void Bfs(int begin);/*breadth-first search,广度优先搜索*/
//vector<int>visited;/*访问标记数组*/
};
邻接矩阵的构建函数:void Creadjm();/*简单结构建立图的邻接矩阵*/
void _adjMatrix::Creadjm() {
int i, j, k;
cout << "<Input vexnum ,kind and edgenum>" << endl;
cout << "vertex_number: "; cin >> vexnum;
if (vexnum > MAX)cout << "Your sizes out of range";
cout << "kind(directed: 1, undirected: 0): "; cin >> kind;
cout << "edges_number: "; cin >> edgenum;
//判断边数是否合法,有向完全图边数是无向的两倍
//n个顶点的完全图边数公式:edges=n(n-1)/2
if (kind == 0 && edgenum > vexnum * (vexnum - 1) / 2)
cout << "Your edges out of range";/*if待完善*/
if (kind == 1 && edgenum > vexnum * (vexnum - 1))
cout << "Your edges out of range";/*if待完善*/
/*赋值代表顶点的字符,>='A',<='Z'*/
for (k = 0; k < vexnum; ++k)vex.push_back('A' + k);
visited.resize(vexnum); /*初始化"访问标记"数组*/
edge.resize(vexnum); /*初始化邻接矩阵*/
for (i = 0; i < vexnum; ++i) /*初始化邻接矩阵*/
edge[i].resize(vexnum); /*初始化邻接矩阵*/
/*输入每条边*/
cout << "<Input " << edgenum << " edges : (instance: 1 2)>" << endl;
int edges = edgenum;
for (k = 0; k < edgenum; ++k) {
cout << "(resiude: " << edges-- << " edges): ";
cin >> i >> j; /*输入边或弧*/
if (i >= vexnum || j >= vexnum)
cout << "Error: out of range";/*if待完善*/
edge[i][j] = 1;
if (kind == 0)edge[j][i] = 1; /*若是有向图,则取消该赋值语句*/
}
}
运行,输入以下数据:
按上述步骤完成创建,系统会生成一个存放元素值的一维数组:
vex[] | A | B | C | D | E |
---|
还有一个存放顶点关系的二维数组 edge[][] :
vex[] \ edge[][] | [0] | [1] | [2] | [3] | [4] |
---|---|---|---|---|---|
A | 0 | 1 | 1 | 0 | 0 |
B | 1 | 0 | 0 | 1 | 1 |
C | 1 | 0 | 0 | 0 | 1 |
D | 0 | 1 | 0 | 0 | 0 |
E | 0 | 1 | 1 | 0 | 0 |
图的邻接矩阵存储就简单完成了。
二、图的遍历
简介:与线性表及树的遍历类似,图的遍历就是按照某种顺序依次访问图中的所有顶点,而且每个顶点仅访问一次。
深度优先搜索
深度优先搜索 (Depth first search) 是指在图中任选一个出发点v并访问;从顶点v的邻接点中按指定顺序选择一个未曾访问过的顶点w作为新的出发点,继续进行深度优先搜索;若w的所有邻接点均已经访问过,则回溯到前一个访问过的顶点继续进行深度优先搜索,直到图中所有和v有路径相通的顶点均被访问过为止。若此时图中仍有未访问的顶点,则另选一个尚未访问过的顶点作为新的出发点,重复上述过程,直到图中所有的顶点均已被访问为止。
模型图(G图为例):
1.从n号下标出发,访问vex[n];
2.依次循环edge[n][i++];
3.当循环遇到edge[0][i]的值为1的元素(且没有访问标记,即visited[i]=0),此时将元素下标i作为参数继续调用此函数,依次递归;
4.i>=vexnum时退出循环,等起点列的循环结束后,搜索visited数组,检查元素是否都被标记;
5.若存在visited[n]为0,则将n作为参数重复1~4的步骤。
C++代码实现:
/*Depth-first search,深度优先搜索 (基于邻接矩阵存储结构) */
int _adjMatrix::Dfs(int now) {
static int flag = 0; //静态变量标记递归位置,0为起始位置
cout << vex[now]; //访问
visited[now] = 1; //置访问标记
for (int i = 0; i < vexnum; ++i) //循环访问当前(now)列的元素
if (edge[now][i] == 1 && visited[i] == 0) {
flag++; //递归前取消起始位置(flag=0)的标记
Dfs(i); //递归搜索
}
if (flag != 0)return --flag;//若为起点则取消,继续搜索不连通的下一部分
//若不为起点则回溯到上一个访问过的顶点
for (int i = 0; i < vexnum; ++i) //搜索不连通的其他部分
if (visited[i] == 0)Dfs(i);
}
广度优先搜索
广度优先搜索 (Breadth first search) 是指在图中任选一个出发点v,并访问之;接着依次访问顶点v的所有邻接点,然后再分别指定v的每个邻接点作为出发点继续进行广度优先搜索,依次类推,直到图中所有和v有路径相通的顶点都已被访问为止。若此时图中仍有未访问的顶点,则另选一个尚未访问的顶点作为新的出发点,重复上述过程,直到图中所有顶点均已被访问为止。模型图(G图为例):
1.把初始元素入队
2.循环:
(1)判断队空,非空进入循环,队空循环结束
(2)队头元素n出队,访问 vex[n],并将edge[n][i++]中值为1(且入队标记数组中对应值为0,即visited[i]==0)的列下标入队,同时在入队标记数组中标记此下标。
3.在入队标记数组visited[]中搜索尚未被标记的下标,若存在则作为参数重复1~3步骤。
C++代码实现:
/*Breadth-first search,广度优先搜索 (基于邻接矩阵存储结构) */
void _adjMatrix::Bfs(int begin) {
static deque<int>queue;
queue.push_back(begin); //初始顶点入队
visited[begin] = 1; //置初始入队标记
while (!queue.empty()) { //队列不空
cout << vex[queue.front()]; //访问
int popnum = queue.front(); //记录即将出队的元素
queue.pop_front(); //出队
for (int i = 0; i < vexnum; ++i) //遍历第(出队元素值)列
//若存在边(值为1)且无入队标记
if (edge[popnum][i] == 1 && visited[i] == 0) {
queue.push_back(i); //入队
visited[i] = 1; //置入队标记
}
}
for (int i = 0; i < vexnum; ++i) //搜索不连通的其他部分
if (visited[i] == 0)Bfs(i);
}
其他
调试:
头文件:
#include <iostream>
#include<vector>
#include<deque>
#include<Windows.h>
using namespace std;
/*亮黄色*/ #define _yellowText SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED) /*亮黄色*/
/*正常白色*/ #define _white SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY)
#define MAX 10
测试函数:
int main()
{
_adjMatrix G;
_yellowText; cout << "<Create a graph> " << endl;_white;
G.Creadjm();
_yellowText; cout << "<Output adjacent matrix>" << endl; _white;
G.list();
_yellowText; cout << "<Depth-first search from zero>"<<endl; _white;
G.Dfs(0); cout << endl << endl;
G.visited.clear();
G.visited.resize(G.vexnum);
_yellowText; cout << "<Breadth-first search from zero>" << endl; _white;
G.Bfs(0); cout << endl;
}
参考文献:
《数据结构(C语言版)(第3版)》:第6章 图
啊写完,没写容错
好累,不写了,光速逃离,咻 !~
推荐阅读
-
Python数据结构与算法之图的广度优先与深度优先搜索算法示例
-
Java数据结构与算法:图、图的概念、深度优先搜索DFS、广度优先搜索BFS、思路分析、代码实现
-
【数据结构】图的遍历——深度优先搜索DFS、广度优先搜索BFS
-
数据结构与算法————图的遍历DFS深度优先搜索和BFS广度优先搜索
-
go语言编程学习实现图的广度与深度优先搜索
-
python实现图的深度优先搜索(DFS)和广度优先搜索(BFS)算法及Dijkstra最短路径应用
-
实验3.2:以邻接矩阵为存储结构的图的深度、宽度优先遍历
-
邻接表为存储结构 | 实现图的深度、宽度优先遍历
-
数据结构(图)——简单无向图的邻接矩阵,实现广度优先遍历
-
邻接矩阵无向图的深度优先遍历C/C++代码实现