hnust 1966: 广度优先搜索
题目描述
输入一个图,用邻接表存储(实际上也可以选择邻接矩阵),并实现BFS操作。
拷贝前面已经实现的代码,主函数必须如下,完成剩下的部分。
int main()
{
Graph g;
CreateUDG(g);
BFS(g, 0);//从0号顶点开始遍历
DestroyUDG(g);
return 0;
}//main
输入
输入的第一行是两个整数,分别是图的总顶点数n和总边数e
第二行是n个空格分开的字符串,是顶点的名字,依次对应编号0~n-1。
随后有e行,每行两个空格分开的顶点名字,表示一条边的两个顶点。
具体见样例。
输出
输出图的BFS序列,遍历次序按教材,每个顶点后面跟一个空格。
具体见样例。
样例输入 Copy
8 9
v1 v2 v3 v4 v5 v6 v7 v8
v1 v2
v1 v3
v2 v4
v2 v5
v3 v6
v3 v7
v4 v8
v5 v8
v6 v7
样例输出 Copy
v1 v2 v3 v4 v5 v6 v7 v8
提示
样例对应教材6.5的图G4
解题过程
注:本题按照书上算法解析完成,广度优先搜索的细化代码及函数分解请看合集《2024.6 hnust 23级 数据结构 课程设计》“推箱子游戏-广度优先搜索版本”
图的广度优先搜索(BFS)需要借助到队列来遍历:
①首先,选取图中某一顶点vi作为出发点,访问后将其入队并标记为已访问(使用队列用于避免重复访问,存放已经访问过的各邻接顶点);
②依次访问与vi邻接的顶点,即当队列不为空时检查出队顶点的所有邻接顶点,访问未被访问的邻接顶点并将其入队,重复该过程;【可概括为由起始顶点开始,按照广度优先的顺序逐层遍历与当前顶点相邻的顶点将其访问】
③当队列为空时跳出循环,即所有已被访问的顶点的邻接顶点均被访问到,则此时遍历完成。
(二)BFS的空间复杂度和时间复杂度
对于一个图G=(V,E),由顶点集V和边集E组成。
1、BFS算法的空间复杂度
通过BFS遍历的空间复杂度为O(|V|)。
2、BFS算法的时间复杂度
时间复杂度取决于图的存储结构,若通过邻接矩阵表示图,则查找顶点的邻接顶点所需时间为O(|V|),总时间复杂度为O(|V2|)(邻接矩阵为方阵n×n),这和DFS算法的时间复杂度是一样的;若通过邻接表表示图,则每个顶点都入队一次,即所需时间为O(|V|),搜索顶点的邻接顶点所需时间为O(|E|),其时间复杂度为O(|V|+|E|)。
这段C++代码实现了一个基于广度优先搜索(BFS)的无向图的拓扑排序算法。以下是对代码的详细解析:
-
头文件和命名空间:
- 包含
<bits/stdc++.h>
头文件,它包含了标准库中的大部分内容。 - 使用
using namespace std;
来避免在标准库类型和函数前加std::
。
- 包含
-
读取图的参数:
- 读取两个整数
n
和e
,分别代表图中顶点的数量和边的数量。
- 读取两个整数
-
存储顶点信息:
- 创建一个字符串数组
nodes
来存储顶点的名称。
- 创建一个字符串数组
-
输入顶点名称:
- 使用循环读取
n
个顶点的名称。
- 使用循环读取
-
建立顶点位置映射:
- 使用
map
(映射)来存储每个顶点名称与其在数组中的索引位置。
- 使用
-
创建无向图:
- 使用
map
来创建一个邻接表G
,表示无向图中的边。
- 使用
-
对邻接表进行排序:
- 对每个顶点的邻接表进行排序,以便在后续的搜索中按顺序访问。
-
初始化访问标记:
- 使用
map
初始化所有顶点的访问状态为未访问。
- 使用
-
使用队列实现BFS:
- 使用
queue
来实现BFS,首先将起始顶点压入队列,并标记为已访问。
- 使用
-
BFS搜索:
- 当队列不为空时,执行循环:
- 弹出队列前端元素作为当前访问的顶点,并输出。
- 遍历当前顶点的所有邻接顶点。
- 如果邻接顶点未被访问,将其标记为已访问,并压入队列。
- 当队列不为空时,执行循环:
-
输出访问顺序:
- 在访问过程中,每访问一个顶点,就输出其名称。
-
程序结束:
- 当队列为空时,表示所有顶点都被访问完毕,程序结束。
代码逻辑分析:
- 这段代码通过BFS实现了拓扑排序,适用于无向图。
- 使用队列来存储待访问的顶点,使用映射来记录顶点的访问状态和位置信息。
潜在问题:
- 代码中注释掉的部分提供了另一种实现拓扑排序的方法,但在当前实现中未使用。
改进建议:
- 考虑使用
std::vector
替代数组,以提高代码的安全性和灵活性。 - 考虑使用
std::unordered_map
替代std::map
,以提高查找效率。 - 如果需要处理更大的图或更复杂的图结构,可以考虑优化内存使用和搜索算法。
- 代码中的排序部分可能不是必要的,因为拓扑排序的目的是线性化图,而不是排序顶点。如果不需要对顶点进行排序,可以省略排序逻辑。
AC代码
#include<bits/stdc++.h>
using namespace std;int main(int argc, char const *argv[])
{map < string , std::vector<string> > G;int n ,e;cin>> n >> e; //输入:8 9string nodes[n];for (int i = 0; i < n; ++i){cin >> nodes[i]; //输入:v1 v2 v3 v4 v5 v6 v7 v8}map <string , int > location;for (int i = 0; i < n; ++i) //排序数组{location[nodes[i]] = i;}for (int i = 0; i < e; ++i) //创建无向图{string l,r;cin >> l >> r;G[l].push_back(r);G[r].push_back(l);}for (int i = 0; i < n; ++i) //对无向图的排序{int j = G[nodes[i]].size();for (int p = 0; p < j; ++p){for (int q = 0; q < j; ++q){if(location[G[nodes[i]][q]] > location[G[nodes[i]][p]]){string temp;temp = G[nodes[i]][q];G[nodes[i]][q] = G[nodes[i]][p];G[nodes[i]][p] = temp;}}}}map < string , bool > vis; //是否被访问queue < string > q;q.push(nodes[0]);vis[nodes[0]] = true;// string rev[n];// int r = 0;while(q.size()){string curr;curr = q.front(); q.pop();// rev[r++] = curr;cout << curr << " ";for(auto next : G[curr]){if(!vis[next]){vis[next] = true;q.push(next);}}}return 0;
}