浙大数据结构的编程题——-图-列出连通集

数据结构-图-列出连通集

给定一个有N个顶点和E条边的无向图,请用DFS和BFS分别列出其所有的连通集。假设顶点从0到N−1编号。进行搜索时,假设我们总是从编号最小的顶点出发,按编号递增的顺序访问邻接点。

输入格式:

输入第1行给出2个整数N(0<N≤10)和E,分别是图的顶点数和边数。随后E行,每行给出一条边的两个端点。每行中的数字之间用1空格分隔。

输出格式:

按照"{ v​1 ​​ v ​2 ​​ … v ​k ​​ }“的格式,每行输出一个连通集。先输出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宽度优先搜索算法(又称广度优先搜索)是最简便的图的搜索算法之一,这一算法也是很多重要的图的算法的原型。Dijkstra单源最短路径算法和Prim最小生成树算法都采用了和宽度优先搜索类似的思想。其别名又叫BFS,属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。

两者区别

深度优先搜索用栈(stack)来实现,整个过程可以想象成一个倒立的树形:

  1. 把根节点压入栈中。
  2. 每次从栈中弹出一个元素,搜索所有在它下一级的元素,把这些元素压入栈中。并把这个元素记为它下一级元素的前驱。
  3. 找到所要找的元素时结束程序。
  4. 如果遍历整个树还没有找到,结束程序。
  5. 类似于树的前序遍历

其伪代码如下:

DFS(int u){                                 //DFS函数
    vis[u]=true;                            //用数组vis记录结点是否被访问
    for(从u出发所能到达的所有顶点v){            //枚举所有u能到达的顶点v
        if(vis[v]==false){                  //如果未被访问,递归进行访问
            DFS(v);
        }
    }
}

DFSTrave(G){                                //DFS的驱动函数
    for(G的所有顶点u){                        //对每一个连通分量都进行DFS遍历
        if(vis[u]==false){
            DFS(u)
        }
    }
}

广度优先搜索使用队列(queue)来实现,整个过程也可以看做一个倒立的树形:

  1. 把根节点放到队列的末尾。
  2. 每次从队列的头部取出一个元素,查看这个元素所有的下一级元素,把它们放到队列的末尾。并把这个元素记为它下一级元素的前驱。
  3. 找到所要找的元素时结束程序。
  4. 如果遍历整个树还没有找到,结束程序。
  5. 类似于树的层序遍历

其伪代码如下:

BFS(u){
    queue q;
    q.push(u);                                  //起始顶点入队并标记
    vis[u]=ture;
    while(队列q不空){
        取出队首元素u进行访问;
        for(从U出发可达到的所有顶点v){
            if(vis[v]==false){                  //如果v未曾加入过队列,入队并标记 
                将v入队;
                vis[v]=ture; 
            }
        } 
    } 
} 
BFSTrave(G){
    for(G的所有顶点u,依此访问){
        if(vis[u]==false){
            BFS(u);
        }
    } 
} 

解题思路

首先要建立图,存储图。表示图主要有两种方式:

  1. 邻接矩阵G[N][N]:
    1. 用二维数组表示,直观易于理解。
    2. 方便检查任意一对顶点间是否存在边
    3. 方便查找任一顶点的所有“邻接点”(有边相连的顶点)
    4. 方便计算任一顶点的“度”(对应行列非0元素个数)
    5. 缺点:稀疏图浪费空间,数边时间复杂度较高
  2. 邻接表 G[N]为指针数组,对应矩阵每行一个链表,只存非0元素。
    1. 方便查找任一顶点的所有“邻接点”(有边相连的顶点)
    2. 节约稀疏图空间
    3. 方便计算任一顶点的“度” ,其对应链表元素个数
    4. 缺点:检查顶点间是否存在边需要遍历对应数组

建立图后,先输出DFS的结果,再输出BFS的结果 在每次有新的结点被遍历后,将新的被遍历的结点输出。

Code

#include <iostream>
#include <string>
#include <vector>
#include <list>
#include <map>
#include <algorithm>
#include <iterator>
#include <math.h>
#include <stack>
#include <queue>
using namespace std;

#define Graph vector<vector<int>>

void DFS(Graph G,int node, vector<int> &visited) {
	visited.push_back(node);
	for (int i = 0 ; i < G.size(); i++) {
		if (G[node][i] == 1 ) {//枚举所有u能到达的顶点v,如果未被访问,递归进行访问
			if(find(visited.begin(),visited.end(),i)== visited.end())
				DFS(G, i, visited);
		}
	}
}

void BFS(Graph G, int node, vector<int> &visited) {
	queue<int> q;
	q.push(node);
	while (!q.empty())
	{
		int u = q.front();
		//u被访问过,则跳出,
		if (find(visited.begin(), visited.end(), u) != visited.end()) {
			q.pop();
			continue;
		}
		//u未被访问过,访问u
		visited.push_back(u);
		for (int i = 0; i < G.size(); i++) {
			if (G[u][i] == 1) {//枚举所有u能到达的顶点v,压入queue
				q.push(i);
			}
		}
	}
}

int main() {
	//begin:			end:
	ios::sync_with_stdio(false);
	//建立图,邻接矩阵
	int N,E; //分别是图的顶点数和边数
	cin >> N >> E;
	Graph G(N, vector<int>(N,0));//初始化

	int x, y;
	for (int i = 0; i < E; i++) {
		cin >> x >> y;
		G[x][y] = 1;
		G[y][x] = 1;
	}
	//DFS
	int DFS_out=0;//可以存个数
	vector<int> visited;
	for (int i = 0; i < N; i++) {
		if (find(visited.begin(), visited.end(), i) == visited.end()) {
			DFS(G, i, visited);
			if (visited.size() > DFS_out) {//如有新访问的结点,则输出
				cout << "{ ";
				for (int i = DFS_out; i < visited.size(); i++) {
					cout << visited[i] << " ";
				}
				DFS_out = visited.size();
				cout << "}" << endl;
			}
		}
	}

	//BFS
	int BFS_out = 0;
	visited.clear();
	for (int i = 0; i < N; i++) {
		if (find(visited.begin(), visited.end(), i) == visited.end()) {
			BFS(G, i, visited);
			if (visited.size() > BFS_out) {//如有新访问的结点,则输出
				cout << "{ ";
				for (int i = BFS_out; i < visited.size(); i++) {
					cout << visited[i] << " ";
				}
				BFS_out = visited.size();
				cout << "}" << endl;
			}
		}
	}
	return 0;
}