最短路Dijstra算法(复杂节点,优先队列,C++)


前言

最短路径算法在众多领域都有广泛应用,众多网络流问题都基于或部分基于最短路问题。一种经典的有效解决最短路问题的算法——dijkstra 算法已经得到广泛应用。dijkstra算法是一种label setting算法,适用于一对多,即某一点到其他各点的最短路。通常情况下的最短路算法依赖于邻接矩阵,用以描述网络的拓扑结构。然而在多数情况下,网络中点包含了多个特征,如位置,前后节点等。 本文用结构体(类也可以)来表示网络中的节点,用C++语言实现dijkstra算法。


一、节点(Node)的数据结构

节点可以采用结构体来描述,这样能够适配绝大多数情况下的应用,并且不必专门去构建邻接矩阵。这里的label指代了网络中的节点(因为dijkstra也是label setting算法,所以这里命名为label)。Id表示节点编号,flag表示该点是否已经成为永久标号的点(也即closed set中的点),lastid表示当前节点的沿着“当前最短路”的前一个节点的标号,path用来存储最短路径。后面重载了若干构造函数,可根据实际情况进行增减。

struct Label {
	int id;
	bool flag; // 是否已经成为closed set中的点
	double val;
	int lastid; 
	vector<int> path; // 用来存储路径
	Label(int id) {
		this->id = id;
		this->flag = false;
		this->val = 500000.0;
	}
	Label(int id, double val) {
		this->id = id;
		this->val = val;
	}
	Label(int id, double val, bool flag) {
		this->id = id;
		this->flag = flag;
		this->val = val;
	}
};

二、Dijkstra算法实现

1. 初始化,确定从哪一点开始寻路

	int startnode = wb.getNodeID();
	int benchid = wb.getID();
	
	vector<Label> label = vector<Label>(98 * 98 + 1);
	for (int i = 0; i < label.size(); i++)
	{
		label[i] = Label(i);
	}
	label[startnode].val = 0;
	label[startnode].lastid = 0; // 默认第一个阶段的前节点是0

2. 优先队列

struct cmp {
	bool operator()(Label a, Label b)
	{
		return a.val > b.val;
	}
};

priority_queue<Label, vector<Label>, cmp> pq;
pq.push(Label(startnode, 0.0));

采用优先队列能够在每次寻找最小标号时更快的找到,时间复杂度为O(logn)。利用STL实现一个优先队列需要手写一个cmp比较函数,具体写法可以参考相关文档。将第一个节点push进队列,每次出队的时候默认时val最小的标号。


3. 算法主体

	while (!pq.empty())
	{
		int topnode = pq.top().id;
		if (label[topnode].flag == true) {
			pq.pop();
			continue;
		}
		else
		{
			label[topnode].flag = true;
			label[topnode].path = label[label[topnode].lastid].path; // 被选中的最短的node 的路径是前继节点的路径加上当前节点
			label[topnode].path.push_back(topnode);
			nodeset[topnode].path[benchid] = label[topnode].path; // 将确定的path赋值给 nodeset中的node
			MapDis[benchid][topnode] = pq.top().val;
			//cout << MapDis[benchid][topnode] << endl;
			//cout << topnode << " " << endl;
			for (int i = 0; i < nodeset[topnode].nextnode.size(); i++)
			{

				int tmp = nodeset[topnode].nextnode[i]; // tmp 是 topnode 的后继结点
				if (label[tmp].flag == true)
				{
					continue;
				}
				if (abs(tmp - topnode) == 97 || abs(tmp - topnode) == 99) {
					if (label[topnode].val + 0.5 * 1.414 < label[tmp].val)
					{
						label[tmp].val = label[topnode].val + 0.5 * 1.414;
						label[tmp].lastid = topnode;
					}
				}
				else {
					if (label[topnode].val + 0.5 < label[tmp].val)
					{
						label[tmp].val = label[topnode].val + 0.5;
						label[tmp].lastid = topnode;
					}
				}

				//pq.push(std::pair<double, int>(label[tmp], tmp));
				pq.push(Label(tmp, label[tmp].val));
			}
			pq.pop();
		}
	}
	//cout << "结束了?" << endl;

这里在记录路径的时候,采用了一边寻路一边记录路径的方式,也在一定程度上避免了回溯所造成的额外的时间消耗。每次被选中的最短的node的路径是前继节点的路径加上当前节点,如果label已经时closed set中的节点就直接continue跳过。

中间一段较长的for循环本质上是更新当前节点的后继节点的标号,可以根据实际情况进行替换。最后当队列为空时,即所有的点都被遍历到了,也即算法完成。当仅想找到从当前到某一点的最短路径,在中间特判并跳出即可。


总结

Dijkstra是网络流算法中最为经典的算法之一,熟练掌握能够应用在诸多实际案例中。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用C++实现Dijkstra算法的示例代码: ```c++ #include <iostream> #include <vector> #include <queue> using namespace std; const int INF = 1e9; // 无穷大 const int MAXN = 1000; // 最大点数 vector<pair<int, int>> adj[MAXN]; // 邻接表存图 int dist[MAXN]; // 起点到各点的最短距离 bool visited[MAXN]; // 标记是否访问过 void dijkstra(int s) // s为起点 { fill(dist, dist + MAXN, INF); // 初始化dist数组为无穷大 fill(visited, visited + MAXN, false); // 初始化visited数组为false dist[s] = 0; // 起点到自身的距离为0 priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq; pq.push(make_pair(dist[s], s)); // 将起点加入优先队列 while (!pq.empty()) { int u = pq.top().second; pq.pop(); if (visited[u]) // 若u已被访问过,则跳过 continue; visited[u] = true; // 标记u已被访问过 for (auto v : adj[u]) { int w = v.first; int weight = v.second; if (dist[u] + weight < dist[w]) // 更新距离 { dist[w] = dist[u] + weight; pq.push(make_pair(dist[w], w)); // 将w加入优先队列 } } } } int main() { int n, m; // n为点数,m为边数 cin >> n >> m; for (int i = 0; i < m; i++) { int u, v, weight; cin >> u >> v >> weight; adj[u].push_back(make_pair(v, weight)); adj[v].push_back(make_pair(u, weight)); // 无向图,因此加入两条边 } int s; // 起点 cin >> s; dijkstra(s); for (int i = 0; i < n; i++) cout << "起点" << s << "到点" << i << "的最短距离为:" << dist[i] << endl; return 0; } ``` 代码中使用了邻接表存图,使用了STL中的优先队列来维护距离最小值,时间复杂度为$O(mlogn)$,其中n为点数,m为边数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值