朴素版Dijkstra和堆优化版Dijkstra算法

1、朴素Dijkstra算法
时间复杂度: O ( n 2 ) O(n^2) O(n2) 适合稠密图(邻接矩阵存) 边数=n^2

思路:
构造一个最短路径数组,每次找到数组中未访问的节点里最小的点
以上一步的节点为最新节点,更新起始点到所有点的距离
以此类推,直到所有点都被遍历。

伪代码:
初始化state数组(state数组保存所有点的 已确定的最短距离)
1号点的距离初始化为零,其他点初始化成无穷大:dist[1]=0 dist[other]=+∞
循环:每次循环都能确定一个点的最短距离,循环n次,求出每一个点到起点的最短距离
for j:0~n
遍历dist数组 找到一个没有确定最短路径的点state[j]=false,且该点距离源点最近
state[t]=true,标记点t已确定最短距离
用t更新他指向的其他点x的距离
判断从1走到x和从1走到t再走到x的距离,如果比较短,就更新x的距离
在这里插入图片描述

图片来自网络,侵删

#include <iostream>      
#include <cstring>
#include <algorithm>


using namespace std;
const int N = 510;

const int INF = 0x3f3f3f3f;

int n, m;

// 稠密图,邻接矩阵存图
int g[N][N];
// 表示每个点当前,从1号点走到他自己的最短距离
int dist[N];
// 每个点的最短路是否已经确定
int st[N];

int dijkstra()
{
   // 初始化距离
   memset(dist, 0x3f, sizeof dist);
   dist[1] = 0;
   // 有n个点所以要进行n次 迭代
   for (int i = 0; i < n; i++)
   {
   	int t = -1;
   	// 找到一个 距离源点最近的点,且这个点还没有确定最短路径st[j]=fasle 
   	for (int j = 1; j <= n; j++)
   	{
   		// 当前这个点还没有确定最短路径
   		// 且
   		// 源点到当前这个点的距离是最短的
   		if (st[j] == false && (t == -1 || dist[t] > dist[j]))
   		{
   			// 找到了这个点j
   			t = j;
   		}
   	}
   	// 把t加到st里面去,标记这个点已经确定最短距离
   	st[t] = true;
   	// 用当前这个点更新其他点的距离
   	for (int j = 1; j <= n; j++)
   	{
   		// 1到j这个点的距离,和1到t再从t到j这个点的距离比大小
   		dist[j] = min(dist[j], dist[t] + g[t][j]);
   	}

   }

   // 1和n不联通
   if (dist[n] == INF)
   {
   	return -1;
   }
   else
   {
   	return dist[n];
   }

}

int main()
{
   cin >> n >> m;
   memset(g, 0x3f, sizeof g);
   while (m--)
   {
   	int a, b, c;
   	cin >> a >> b >> c;
   	// 处理重边,两条边只保留一条距离最短的边 
   	g[a][b] = min(g[a][b], c);
   }

   int t = dijkstra();
   cout << t;
   return 0;
}

2、堆优化版的Dijkstra算法
时间复杂度:O(mlogn) 适合稀疏图(邻接表存) 边数=10^5
朴素版中,每次循环都要遍历dist数组,找到距离源点最近的点t
对优化版相比朴素版,用堆优化了 找到点t的操作
思路:

  1. 一号点的距离初始化为零,其他点初始化成无穷大。
  2. 将一号点放入堆中。
  3. 不断循环,直到堆空。每次循环中:
    弹出堆顶,并标记该点的最短路径已经确定。
    用该点更新其它点的距离,若更新成功就加入到堆中。
#include <iostream>      
#include <cstring>
#include <algorithm>
#include <queue>


using namespace std;
const int N = 1000010;

const int INF = 0x3f3f3f3f;

int n, m;

// 节点的距离和编号
typedef pair<int, int> PII;

// 稀疏图,邻接表存图
int h[N], e[N], ne[N], idx;
int w[N];// 每条边的权重
// 表示每个点,当前为止,从1号点走到他自己的最短距离
int dist[N];
// 每个点的最短路是否已经确定
int st[N];

int dijkstra()
{
	// 初始化距离
	memset(dist, 0x3f, sizeof dist);
	dist[1] = 0;
	// 定义一个小根堆
	priority_queue<PII, vector<PII>, greater<PII>> heap;
	// 放入0号点,
	// 0,1这个顺序不能倒,pair排序时是先根据first,再根据second
	heap.push({ 0,1 });
	while (heap.size())
	{
		// 取不在集合S中距离最短的点
		PII t = heap.top(); 
		heap.pop();
		// ver节点编号
		int ver = t.second;
		int distance = t.first;
		
		if (st[ver])
			continue;

		st[ver] = true;

		for (int i = h[ver]; i != -1; i = ne[i])
		{
			// i只是个下标,e中在存的是i这个下标对应的点。
			int j = e[i];
			// 1号点到当前这个带的距离大于从1到t再到这个点的距离
			if (dist[j] > distance + w[i])
			{
				// 更新j点的距离
				dist[j] = distance + w[i];
				// 把j这个点放到优先对列
				heap.push({ dist[j],j });
			}
		}
	}

	// 1和n不联通
	if (dist[n] == INF)
	{
		return -1;
	}
	else
	{
		return dist[n];
	}

}

// 添加一条边 a指向b
// c是边的权重
void add(int a, int b, int c)
{
	w[idx] = c;
	e[idx] = b;
	ne[idx] = h[a];
	h[a] = idx;
	idx++;
}

int main()
{
	cin >> n >> m;
	memset(h, -1, sizeof h);

	while (m--)
	{
		int a, b, c;
		cin >> a >> b >> c;
		// 邻接表存图不需要对重边进行处理
		add(a, b, c);
	}

	int t = dijkstra();
	cout << t;
	return 0;
}
  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值