dijkstra算法

问题引出

如图,从A城市到D城市的最短距离是多少?
最短路径

解题步骤

  1. 首先初始化所有点到A店的距离为无穷大
  2. 从A点开始,通过A点更新其余点(或A相连的点),并且找到距离A点最近的那个点(这里是点B),作为下一次循环的起始点。
  3. 通过点B,更新与B相连的所有点,并且找到距离B点最近的那个点(这里是F),作为下一次循环的起始点。依次循环n - 1次(n是点的个数)。

过程分析(待续)

  1. 每一次循环都能找到距离A点相对近的那个点,那么最多循环n - 1次就能找到D点到A点的最短距离。
  2. 可能还没循环到n - 1次就找到D点,因此可以提前终止。
  3. 时间复杂度分析:
    1. 朴素版:n次循环依次找到距离A点最近的点,每次循环需要循环n次用于更新
    2. 堆优化版:n次循环依次找到距离A点最近的点,每次循环中找与该点(A点)相连的点(i点),如果路径(A->i)可以被更新,则将i点放入小顶堆。由于自带的堆不支持删除操作,且同一个点可能被不同的点更新,所以需要使用st数组记录i点是否被更新。

自定义关键字

N:最大节点个数。
M:最大有向边个数
n:输入节点个数
m:输入有向边个数
dist[N]:i号点距离原点的最短距离
st[N]:i号点是否被放入小顶堆。0表示未放入,1表示放入。
g[N][N]:g是邻接矩阵,g[a][b]表示从a点到b点的最短距离。

代码

朴素版

#include<iostream>
#include<cstring>
using namespace std;

const int N = 510, INF = 0x3f3f3f3f;
int g[N][N], dist[N];
bool st[N];

int dijkstra(int n, int m) {
	memset(dist, INF, sizeof dist);
	dist[1] = 0;
	for (int i = 0; i < n; i++) {
		//寻找距离原点最近的点
		int t = -1;
		for (int j = 1; j <= n; j++)
			if (!st[j] && (t == -1 ||dist[j] < dist[t]))
				t = j;

		//如果该点就是目标点,则退出循环
		if (t == n) break;

		//标记该点为相对最近点
		st[t] = true;

		//通过该点更新与其他点之间的距离
		for (int j = 1; j <= n; j++)
			dist[j] = min(dist[j], dist[t] + g[t][j]);
	}
	if (dist[n] == INF) return -1;
	return dist[n];
}

int main() {
	memset(g, INF, sizeof g);
	int n, m;
	cin >> n >> m;
	for (int i = 0; i < m; i++) {
		int a, b, c;
		scanf("%d%d%d", &a, &b, &c);
		g[a][b] = min(g[a][b], c);
	}
	cout << dijkstra(n, m);
	return 0;
}

堆优化版

#include<iostream>
#include<queue>
#include<cstring>
using namespace std;

typedef pair<int, int> PII;

const int N = 100010, M = 100010, INF = 0x3f3f3f3f;
int e[N], ne[M], h[N], w[M], idx;
int dist[N];
bool st[N];

void add(int a, int b, int c) {
	e[idx] = b;
	ne[idx] = h[a];
	w[idx] = c;
	h[a] = idx++;
}

int dijkstra(int n) {
	memset(dist, INF, sizeof dist);
	dist[1] = 0;
	//创建小顶堆
	priority_queue<PII, vector<PII>, greater<PII>> heap;
	PII tmp;
	tmp.first = 0, tmp.second = 1;
	heap.push(tmp);
	while (heap.size()) {
		PII t = heap.top();
		heap.pop();
		int d = t.first, ver = t.second;
		if (st[ver]) continue;
		st[ver] = true;
		//更新与之相连的节点
		for (int i = h[ver]; i != -1; i = ne[i]) {
			int j = e[i];
			if (dist[j] > dist[ver] + w[i]) {
				dist[j] = dist[ver] + w[i];
				PII tmp;
				tmp.first = dist[j], tmp.second = j;
				heap.push(tmp);
			}
		}
	}
	if (dist[n] == 0x3f3f3f3f) return -1;
	return dist[n];
}

int main() {
	memset(h, -1, sizeof h);
	int n, m;
	cin >> n >> m;
	for (int i = 0; i < m; i++) {
		int a, b, c;
		scanf("%d%d%d", &a, &b, &c);
		add(a, b, c);
	}
	cout << dijkstra(n);
	return 0;
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值