列出连通集(DFS及BFS遍历图) -- 数据结构
题目:
7-1 列出连通集 (30 分)
给定一个有N个顶点和E条边的无向图,请用DFS和BFS分别列出其所有的连通集。假设顶点从0到N−1编号。进行搜索时,假设我们总是从编号最小的顶点出发,按编号递增的顺序访问邻接点。
输入格式:
输入第1行给出2个整数N(0<N≤10)和E,分别是图的顶点数和边数。随后E行,每行给出一条边的两个端点。每行中的数字之间用1空格分隔。
输出格式:
按照 “ { v1, v2, v3, ... ,vk } ”的格式,每行输出一个连通集。先输出DFS的结果,再输出BFS的结果。
1 v2 ... vk }"的格式,每行输出一个连通集。先输出DFS的结果,再输出BFS的结果。
输入样例:
8 6
0 7
0 1
2 0
4 1
2 4
3 5
输出样例:
{ 0 1 4 2 7 }
{ 3 5 }
{ 6 }
{ 0 1 2 7 4 }
{ 3 5 }
{ 6 }
分析:
当读完这道题之后,很容易就能够知道我们需要做的两件事:①构建图 ②DFS遍历和BFS遍历
这里为了方便起见(同时数据量并不大),这里将采用邻接矩阵存储图。
代码:
对结构体结构的定义:
#define max 10 typedef int vertex_type; typedef int edge_type; typedef struct graph_matrix{ int n, e;//顶点数;边数 vertex_type vexs[max];//顶点一维数组 edge_type edges[max][max];//邻接矩阵二维数组,元素类型为vector<vertex_type> }gm;
全局变量visit[]
//visit[]数组;全局变量 int visit[max] = {0};
①构建图
//创建图 void create_gm(gm &gm) { cin>>gm.n>>gm.e; memset(gm.edges, 0, sizeof(gm.edges)); for(int i=0; i<gm.n; i++){ gm.vexs[i] = i; } //输入边数据 int a, b; for(int i=0; i<gm.e; i++){ cin>>a>>b; gm.edges[a][b] = 1; gm.edges[b][a] = 1; } }
②DFS遍历
//深度优先 //id: 以id为起始点 void DFS(gm &gm, int id) { visit[id] = 1; cout<<id<<" "; for(int i=0; i<gm.n; i++){ if(gm.edges[i][id] == 1 && visit[i] == 0){ DFS(gm, i); } } }
③BFS遍历
//宽度优先 void BFS(gm &gm, int id) { visit[id] = 1; queue<int> qu; qu.push(id); while(qu.size() != 0){ int mark = qu.front(); qu.pop(); cout<<mark<<" "; for(int i=0; i<gm.n; i++){ if(gm.edges[i][mark] == 1 && visit[i] == 0){ visit[i] = 1; qu.push(i); } } } }
全部的代码:
#include<iostream> #include<cstring> #include<queue> using namespace std; #define max 10 typedef int vertex_type; typedef int edge_type; typedef struct graph_matrix{ int n, e;//顶点数;边数 vertex_type vexs[max];//顶点一维数组 edge_type edges[max][max];//邻接矩阵二维数组,元素类型为vector<vertex_type> }gm; //函数声明 void create_gm(gm &gm); void DFS(gm &gm, int id); void BFS(gm &gm, int id); //visit[]数组;全局变量 int visit[max] = {0}; int main() { gm gm;//定义一个叫做gm的邻接矩阵 create_gm(gm); //调用DFS遍历 for(int i=0; i<gm.n; i++){ if(visit[i] == 1)continue; cout<<"{ "; DFS(gm ,i); cout<<"}"<<endl; } //重置visit[]数组 memset(visit, 0, sizeof(visit)); //调用BFS遍历 for(int i=0; i<gm.n; i++){ if(visit[i] == 0){ cout<<"{ "; BFS(gm ,i); cout<<"}"<<endl; } } return 0; } //创建图 void create_gm(gm &gm) { cin>>gm.n>>gm.e; memset(gm.edges, 0, sizeof(gm.edges)); for(int i=0; i<gm.n; i++){ gm.vexs[i] = i; } //输入边数据 int a, b; for(int i=0; i<gm.e; i++){ cin>>a>>b; gm.edges[a][b] = 1; gm.edges[b][a] = 1; } } //深度优先 void DFS(gm &gm, int id) { visit[id] = 1; cout<<id<<" "; for(int i=0; i<gm.n; i++){ if(gm.edges[i][id] == 1 && visit[i] == 0){ DFS(gm, i); } } } //宽度优先 void BFS(gm &gm, int id) { visit[id] = 1; queue<int> qu; qu.push(id); while(qu.size() != 0){ int mark = qu.front(); qu.pop(); cout<<mark<<" "; for(int i=0; i<gm.n; i++){ if(gm.edges[i][mark] == 1 && visit[i] == 0){ visit[i] = 1; qu.push(i); } } } }
总结:
在这个题目中主要是对之前在树章节中,对关于树的前序遍历(DFS)和层次遍历(BFS)的知识迁移。
① DFS往往会运用到栈来进行实现,而递归就是利用栈的一个实现方法,递归特性使算法代码简洁的同时,也使算法理解困难,
所以对于我这种初学者来说画图和动手实践是最好的学习方法。
例子:“走迷宫,你没有办法用分身术同时走进多叉道路中,不撞南墙不回头。”(来源自网络)
② BFS则是要运用到队列,程序流程相对于DFS来说更为清晰。
例子:“当你眼镜掉了,你趴在地上找,你总是会先摸靠近你的地方,如果没有,再摸远一点的地方。”(来源自网络)
如何对已有知识的新运用也是能力提升的一种,在此我也对DFS和BFS有了更深一层的理解。
嘻嘻:)
——但少闲人,所以等等。