邻接矩阵实现图的存储,DFS,BFS遍历
图的遍历一般由两者方式:深度优先搜索(DFS),广度优先搜索(BFS),深度优先就是先访问完最深层次的数据元素,而BFS其实就是层次遍历,每一层每一层的遍历。
1.深度优先搜索(DFS)
我一贯习惯有举例的方法来讲,示例如下:红色代表的是正搜索,蓝色代表回溯,最下面为标志数组。
注意:DFS的搜索出来的序列不是每个人都是一样的,根据具体的程序可能出现不同的顺序。
程序设计:由对深度优先搜索的理解,我们可以知道我们从根节点的开始向下搜索,注意题目中给出的是连通的图,在实际情况下可能有非连通的图,图中根节点v1的子节点为v2,v3,我们可以访问它的子节点按照从左到右也可以从右到左,我们这里选择从左到右的方向,先访问v2,访问v2后又以v2作为子节点开始从左到右访问,但是我们可能注意到访问到v5时,如果我们仅仅从图的相关节点的连通性来判断是否应该继续深入的话,我们会在v5节点形成一个环,从而造成程序的死循环,这里就需要一个标志数组来标志哪些节点是已经访问了的,这样不仅仅能够保证不形成环也可以为节点回溯提供保证。说到这里我们基本已经解决了连通图的DFS,我们主要到如果是非连通图我们不能通过子图的连通性来访问到所有的节点,所以下面一段代码就是必须的了;
for(i=0; i<n; i++) if(!color[i])
{
printf(" V%d ", i+1); DFS_Visit(G,i,n);
printf("\n");
}
这段代码从第一个节点测试到最后一个节点测试是否已经访问过,如果是非连通图,中间一定会有在第一次访问完成之后还有节点没有访问,所有利用标志数组就可以轻易的达到这个目的。
上图测试用例.txt
1 2
1 3
2 4
2 5
4 8
5 8
3 6
3 7
6 7
测试程序:
#include <stdio.h>
#include <string.h>
/*
dfs:深度优先搜索,利用了递归的方法,以根结点为起点,逐条分支深度搜索到底(先是节点的最左边分支被搜)
*/ const int GNumber = 8;
int G[GNumber][GNumber];
int color[GNumber]; //标志数组 void DFS_Visit(int G[][GNumber], int i, int n)
{
int j;
color[i] = 1;
for(j=0; j< n; j++) //遍历所有n个节点中,所有与i相同的节点,都将以这些节点为起点继续搜索
{
if(G[i][j] && !color[j])
{
printf(" V%d ", j+1);
color[j] = 1;
DFS_Visit(G, j, n);
}
} } void DFS(int G[][GNumber], int n)
{
int i;
memset(color, 0, sizeof(color));
//n个顶点,每个顶点都要作为起始点尝试搜索访问,不可能总是任何一个顶点开始都能遍历整个图
for(i=0; i<n; i++)
{
if(!color[i]) //顶点i未曾访问过
{
printf(" V%d ", i+1); //首先输出i,然后继续搜索查找/访问与i连通的节点
DFS_Visit(G,i,n);
printf("\n");
}
} } int main()
{
// FILE *fr;
int i,j;
// fr = fopen("图的遍历测试用例.txt","r");
/* if(!fr)
{
printf("fopen failed\n");
return -1;
}
*/
// while(fscanf(fr,"%d%d", &i, &j) != EOF)
while(scanf("%d%d",&i,&j)!=EOF)
{
G[i-1][j-1] = 1; //注意存储图的数组是从0开始编号的
G[j-1][i-1] = 1;
}
DFS(G,GNumber);
// getchar();
return 0;
}
程序结果:
V1 V2 V4 V8 V5 V3 V6 V7
2.广度优先搜索(BFS)
示例如下:红色代表的是正搜索,最下面为标志数组。
程序设计:一个图如果要层次遍历的话,那么他应该是连通图,不然层次没法分,对一个连通图进行层次遍历,我们模拟一下就知道,如上图,当访问了v1节点后,我们就应该访问第二层都为它的子节点,我们这里以顺序从左到右访问,那么应该访问的是v2,v3,为了能够表示访问的顺序,我们这里设置一个先进先出的结构,很明显就是一个队列了,要访问前v1将它放入队列,然后访问v1,并将他的子节点放到队列中:v1,v2,v3;访问了v1后出队输出,我们继续访问队列中的元素,以队列中的元素为根节点找他的子节点并加入到队列中,队列为空。这里的标志数组标志着节点是否进入过队列,这里由于元素很少而且队列中的元素肯定不会超过顶点个数,所以我直接使用的数组来模拟队列。
测试程序:
#include <stdio.h>
#include <string.h>
/*
BFS :广度优先搜索,类似与队列的先进先出顺序进行遍历,因此可以采用队列存储遍历过的节点,然后再按照队列的思想“先进先出”,一次出队
*/ const int GNumber = 8;
int G[GNumber][GNumber];
int color[GNumber]; // 访问标志数组, 防止回环 ( 重复访问 ) struct Queue //用数组模拟队列
{
int queue[GNumber];
int start;
int end;
}MyQueue; void BFS(int G[][GNumber], int n)
{
int j;
MyQueue.queue[MyQueue.end++] = 0; //节点0,入队
color[0] = 1; //访问第一个节点,标识为真 /*
for(j=0; j<n; j++)
{
if(G[i][j] && !color[j])
{
printf(" V%d ", j+1);
color[j] = 1;
MyQueue.queue[MyQueue.end++] = j;
}
}
*/ while(MyQueue.end != MyQueue.start) //直到队列中元素全部出队结束
{
//color[MyQueue.start] = 1;
for(j=0; j<n; j++)
{
if(G[MyQueue.start][j] && !color[j]) //j与队列中当前节点start连通,且未访问过
{
color[j] = 1; //标识j节点访问标志
MyQueue.queue[MyQueue.end++] = j; //j入队
}
}
//关键在于这一句,利用队列先进先出的思想,在while()循环中,逐个,逐层输出节点
printf(" V%d ", MyQueue.queue[MyQueue.start++]+1);
//每次输出一个节点后,起始标志Start++,向前挪动一位
}
} int main(int argc, char **argv)
{
// FILE *fr;
int i,j;
/*
fr = fopen("图的遍历测试用例.txt","r");
if(!fr)
{
printf("fopen failed\n");
return -1;
}
while(fscanf(fr,"%d%d", &i, &j) != EOF)
*/
while(scanf("%d%d",&i,&j)!=EOF)
{
G[i-1][j-1] = 1; //注意存储图的数组是从0开始编号的
G[j-1][i-1] = 1;
}
memset(&MyQueue, 0, sizeof(MyQueue));
memset(color, 0, sizeof(color)); BFS(G,GNumber); // getchar();
return 0;
}
程序结果:
V1 V2 V3 V4 V5 V6 V7 V8
图的dfs与bfs(深搜广搜)c++ STL模板实现
bfs通过检测边发现点,被发现点(但未探索)入队。(被探索是指是否检测过与该点相关联的临近顶点)一个顶点被完全探索当且仅当他的所有边被检测。一个顶点探索完选另一个顶点,被选点应位于被发现但未被探索点队列的队首。待探索点集为空时算法结束。(bfs探索顺序与发现顺序一致,dfs发现后马上探索)
#include <iostream>
#include <cstdio>
#include <list>
#include <vector>
#include <queue>
using namespace std;
int n;
vector< list<int> > graph;
bool visited[100] = {0};
void dfs(int v)
{
list<int>::iterator it;
visited[v] = true;
printf("%5d", v);
for (it = graph[v].begin(); it != graph[v].end(); ++it)
if (!visited[*it])
dfs(*it);
}
void bfs(int v)
{
list<int>::iterator it;
printf("%5d", v);
visited[v] = true;
queue<int> t;
t.push(v);
while (!t.empty())
{
v = t.front();
t.pop();
for (it = graph[v].begin(); it != graph[v].end(); ++it)
if (!visited[*it])
{
printf("%5d", *it);
t.push(*it);
visited[*it] = true;
}
}
cout << endl;
}
int main()
{
//freopen("in.txt", "r", stdin);
cout << "input the vertex num:"<< endl;
cin >> n;
vector< list<int> >::iterator it;
for (int i = 0; i < n; ++i)
{
list<int> il;
int t;
while (cin >> t && t != n)
il.push_back(t);
graph.push_back(il);
}
cout << "result for bfs:" << endl;
bfs(0);
memset(visited, 0, sizeof(visited)); //重新初始化标志数组
cout << "result for dfs:" << endl;
dfs(0);
return 0;
}

按照链表表示输入以下数据:
8
0 1 2 8
1 0 3 4 8
2 0 5 6 8
3 1 7 8
4 1 7 8
5 2 7 8
6 2 7 8
7 3 4 5 6 8 //表示7与3,4,5,6是连通的
最后一个8用来标识这个节点输入结束。可以得到深搜和广搜的结果。
邻接矩阵实现图的存储,DFS,BFS遍历的更多相关文章
- 【algo&ds】6.图及其存储结构、遍历
1.什么是图 图表示"多对多"的关系 包含 一组顶点:通常用 V(Vertex)表示顶点集合 一组边:通常用 E(Edge)表示边的集合 边是顶点对:(v,w)∈ E,其中 v,w ...
- 数据结构上机实验dfs&&bfs遍历图
#include<stdio.h> #include<string.h> #include<stdlib.h> #include<queue> #inc ...
- <数据结构>图的构建与基本遍历方法
目录 建立一个图 邻接矩阵 邻接表 深度优先遍历(DFS) 具体步骤: 第一部分:给定结点u,遍历u所在的连通块的所有结点 第二部分:对图G所有结点进行第一部分的操作,即遍历了图的所有连通分量 伪代码 ...
- 数据结构作业——图的存储及遍历(邻接矩阵、邻接表+DFS递归、非递归+BFS)
邻接矩阵存图 /* * @Author: WZY * @School: HPU * @Date: 2018-11-02 18:35:27 * @Last Modified by: WZY * @Las ...
- 图的bfs遍历模板(邻接矩阵存储和邻接表存储)
bfs遍历图模板伪代码: bfs(u){ //遍历u所在的连通块 queue q; //将u入队 inq[u] = true; while (q非空){ //取出q的队首元素u进行访问 for (从u ...
- C++编程练习(9)----“图的存储结构以及图的遍历“(邻接矩阵、深度优先遍历、广度优先遍历)
图的存储结构 1)邻接矩阵 用两个数组来表示图,一个一维数组存储图中顶点信息,一个二维数组(邻接矩阵)存储图中边或弧的信息. 2)邻接表 3)十字链表 4)邻接多重表 5)边集数组 本文只用代码实现用 ...
- 列出连通集(DFS及BFS遍历图) -- 数据结构
题目: 7-1 列出连通集 (30 分) 给定一个有N个顶点和E条边的无向图,请用DFS和BFS分别列出其所有的连通集.假设顶点从0到N−1编号.进行搜索时,假设我们总是从编号最小的顶点出发,按编号递 ...
- 图的数据结构的实现与遍历(DFS,BFS)
//图的存储结构:const int MAXSIZE = 10;//邻接矩阵template<class T>class MGraph {public: MGraph(T a[], ...
- 数据结构与算法之PHP用邻接表、邻接矩阵实现图的广度优先遍历(BFS)
一.基本思想 1)从图中的某个顶点V出发访问并记录: 2)依次访问V的所有邻接顶点: 3)分别从这些邻接点出发,依次访问它们的未被访问过的邻接点,直到图中所有已被访问过的顶点的邻接点都被访问到. 4) ...
随机推荐
- Spark记录-Spark作业调试
在本地IDE里直接运行spark程序操作远程集群 一般运行spark作业的方式有两种: 本机调试,通过设置master为local模式运行spark作业,这种方式一般用于调试,不用连接远程集群. 集群 ...
- bzoj千题计划190:bzoj4300: 绝世好题
http://www.lydsy.com/JudgeOnline/problem.php?id=4300 f[i] 表示第i位&为1的最长长度 #include<cstdio> # ...
- Mac下配置环境变量重启后不生效解决(.bash_profile vs .bashrc)(bash/zsh下不加载.bashrc问题解决)
参考上一篇文章说明:http://www.cnblogs.com/EasonJim/p/6283094.html 得知加载顺序如下: /etc/profile /etc/paths ~/.bash_p ...
- android tools相关
1.showin 在include 的根节点设置,可一预览效果
- 用matplotlib绘制每次交易的盈亏三角形
用matplotlib绘制每次交易的盈亏三角形 结果: 代码: python def plot_trade_triangle(self): # plot each trade as a trade-t ...
- ASP.NET批量下载文件
一.实现步骤 在用户操作界面,由用户选择需要下载的文件,系统根据所选文件,在服务器上创建用于存储所选文件的临时文件夹,将所选文件拷贝至临时文件夹.然后调用 RAR程序,对临时文件夹进行压缩,然后输出到 ...
- lucene修改索引——(六)
原理: 修改的原理是先删除,后增加一个,这也是常用的一种修改的方式. 删除的文档的id不会被新增加的文档占用,类似于mysql的自增,当删除一个id=2时,以后id=2就是空着的,不会上来一个把2给占 ...
- plsql 查询 卡死问题解决
plsql可以连接数据库,一点查询就卡死.sqlplus可以连接数据库,也可以查询.后经过测试,原来plsql所引用的路由器ip和服务器ip有限制,修改同一网段ip问题解决.但是不知为何不同网段ip可 ...
- selenium玩转svg操作
今天写脚本发现页面有svg结构,里面的元素无法定位,查找很多资料,然后就记录下来 初步尝试直接在页面中获取svg中包含元素的xpath,直接利用selenium方法访问,无法捕获到相关元素信息. SV ...
- Linux下的snmpd.conf配置说明
SNMP(Simple Network Management Protocol,简单网络管理协议)的前身是简单网关监控协议(SGMP),用来对通信线路进行管理.在RHEL中,SNMP的配置文件地址是/ ...