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