无向图的构建,BFS、DFS遍历搜索,Prim、Kruskal最小生成树算法,Dijkstra最短路径算法

前言

在半年之前写过一篇关于无向图的构建以及相关常用算法的实现 → 图的构建以及BFS(广度优先搜索)、DFS(深度优先搜索)、普里姆最小生成树算法、并查集与kruskal算法
,近日对这些算法进行了一个回顾,并且对其中的代码进行了全面的优化。

无向图构建

还是以之前的无向城市图为例:
在这里插入图片描述
上图列出了城市间的连通关系以及城市间的距离信息,下面的算法将围绕这张图进行展开。

使用临接矩阵表示无向图

在计算机中可以通过使用一些比较精妙的方式例如临界表或者临接矩阵来表示图中各节点的连接关系。笔者更加偏向于使用 临接矩阵
体现代码中是通过我们手动传入一个装有城市间信息的数组,然后将这些信息体现到矩阵中。

Relation of Citys 邵阳 长沙 衡阳 深圳 大庆 岳阳
邵阳 0 235 141 697 INF 364
长沙 235 0 190 782 2682 168
衡阳 141 190 0 641 INF INF
深圳 697 190 641 0 3356 INF
大庆 INF 2682 INF 3356 0 2540
岳阳 364 168 INF INF 2540 0

注: 数字表示城市间的距离,单位km;INF 为 infinite 的缩写,表示城市间并不直接相连。

class Graph {
   
   
public:
	int cityNum;
	vector<string> cityName;//存储所有的城市名
	unordered_map<string, int> cityIndex;//kv: 城市名对应的编号
	vector<vector<int>> cityInf;//城市间关系矩阵
};

以上为创建图会使用到的变量以及容器。
cityNum : 记录城市数量
cityName : 存储所有的城市名
cityIndex : 通过城市名查找对应的城市索引
cityInf : 临接矩阵

void Graph::createGraph(vector<string>& citys, vector<cityRel*>& cityRelation) {
   
   
	cityNum = citys.size();
	for (auto& city : citys)//数组拷贝
		cityName.push_back(city);

	/*将cityInf空间设置为 cityNum行,列同行*/
	cityInf.resize(cityNum);
	for (auto& city : cityInf)
		city.resize(cityNum, INFINITE);//默认设置为不可到达

	//对每一个城市进行编号,存储到哈希表中
	int index = 0;
	for (string& city : citys) {
   
   
		cityIndex[city] = index;
		++index;
	}
	//城市间的信息转换到临界矩阵中
	for (auto& city : cityRelation) {
   
   
		//起始城市索引
		int startIndex = cityIndex[city->startCity];
		//终止城市索引
		int endIndex = cityIndex[city->endCity];
		//两城市距离
		int distance = city->distance;

		//由于是无向图,所以两个方向都需要添加上
		cityInf[startIndex][endIndex] = distance;
		cityInf[endIndex][startIndex] = distance;

	}
	//到自身的距离设置为 0
	for (int i = 0; i < cityInf.size(); ++i)
		cityInf[i][i] = 0;

	cout << "城市信息如下:\n\n";
	//输出临界矩阵
	for (auto& city : citys) {
   
   
		cout << "\t" << city;
	}cout << endl;

	for (int i = 0; i < cityInf.size(); ++i) {
   
   
		cout << citys[i] << "  ";
		for (int j = 0; j < cityInf[i].size(); ++j) {
   
   
			if (cityInf[i][j] == INFINITE)//无穷大
				cout << "\t∞";
			else
				cout << "\t" << cityInf[i][j];
		}cout << endl;
	}
	cout << "\n注: 图中数字表示距离,单位: km,其中'∞'表示不可到达" << endl;
}

创建后的临接矩阵如下:
在这里插入图片描述
∞ 使用一个宏定义来表示:

#define INFINITE 99999999

图的遍历

BFS广度优先

广度以及深度搜索的代码非常相似,只是使用的数据结构有一丢丢差别。直接放上代码

void Graph::BFS(string startCity) {
   
   
	queue<int> que;
	que.push(cityIndex[startCity]);
	//记录城市的访问情况: 避免重复加入
	vector<bool> visited(cityNum, false);
	vector<int> path;

	while (que.empty() == 0) {
   
   
		int cur = que.front();
		que.pop();
		path.push_back(cur);
		visited[cur] = true;
		for (int next = 0; next < cityInf.size(); ++next) {
   
   
			//能够到达且没被加入过
			if (visited[next] == false && cityInf[cur][next] > 0 && cityInf[cur][next] != INFINITE) {
   
   
				que.push(next);
				visited[next] = true;//表示已经访问
			}
		}
	}
	cout << "\n广度优先搜索\n\t" << endl;
	for (int i = 0; i < cityNum - 1; ++i) {
   
   
		cout << cityName[path[i]] << " -> ";
	}
	cout << cityName[path[cityNum - 1]] << endl;
}
DFS深度优先

使用STL库中的stack容器实现:

void Graph::DFS(string startCity) {
   
   
	stack<int> stk;
	stk.push(cityIndex[startCity]);
	//记录城市的访问情况: 避免重复加入
	vector<bool> visited(cityNum, false);
	vector<int> path;

	while (stk.empty() == 0) {
   
   
		int cur = stk.top();
		stk.pop();
		path.push_back(cur);
		visited[cur] = true;
		for (int next = 0; next < cityInf.size(); ++next) {
   
   
			//能够到达且没被加入过
			if (visited[next] == false && cityInf[cur][next] > 0 && cityInf[cur][next] != INFINITE) {
   
   
				stk.push(next);
				visited[next] = true;//表示已经访问
			}
		}
	}
	cout << "\n深度优先搜索\n\t" << endl;
	for (int i = 0; i < cityNum - 1; ++i) {
   
   
		cout << cityName[path[i]] << " -> ";
	}
	cout << cityName[path[cityNum - 1]] << endl;

}

递归实现:

void Graph::recurDFS(string startCity, vector<bool>& visited, vector<int>& path) {
   
   
	if (path.size() >= cityName.size() - 1) {
   
   
		//注意,这里在装满所有城市之后不会再进入递归
		//此时就差最后一个城市未装入,startCity即为该城市
		for (int i = 0; i < cityNum - 1; ++i) {
   
   
			cout << cityName[path[i]] << " -> ";
		}
		//将最后一个城市输出即可
		cout << cityName[cityIndex[startCity]] << endl;
		return;
	}

	int cur = cityIndex[startCity];
	visited[cur] = true;
	path.push_back(cur);
	for (int next = 0; next < cityNum; ++next) {
   
   
		if (visited[next] == false && cityInf[cur][next] > 0 && cityInf[cur][next] != INFINITE) {
   
   
			visited[next] = true;
			recurDFS(cityName[next], visited, path);
		}
	}
}
最优二叉生成树
Prim算法
struct cmp {
   
   
	bool operator()(const Edge* a, const Edge* b) {
   
   
		return a->distance > b->distance;
	}
};
void Graph::primAlgorithm(string startCity) {
   
   
	int num = cityNum - 1;
	int cur = cityIndex[startCity];
	vector<bool> visited(cityNum, false);
	
	priority_queue<Edge
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

nepu_bin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值