$\color{green}{<–画图不易,点下这里赞一下吧}$
朴素迪杰斯特拉算法点这里
优化朴素迪杰斯特拉算法
看一下朴素算法的时间复杂度:
for(i:1 ~ n)//n次
{
t <- 没有确定最短路径的节点中距离源点最近的点;//每次遍一遍历dist数组,n次的复杂度是O(n^2)
state[t] = 1;
更新 dist;//每次遍历一个节点的出边,n次遍历了所有节点的边,复杂度为O(e)
}
算法的主要耗时的步骤是从dist 数组中选出:没有确定最短路径的节点中距离源点最近的点 t。只是找个最小值而已,没有必要每次遍历一遍dist数组。
在一组数中每次能很快的找到最小值,很容易想到使用小根堆。可以使用库中的小根堆(推荐)或者自己编写。
代码:
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>//堆的头文件
using namespace std;
typedef pair<int, int> PII;//堆里存储距离和节点编号
const int N = 1e6 + 10;
int n, m;//节点数量和边数
int h[N], w[N], e[N], ne[N], idx;//邻接矩阵存储图
int dist[N];//存储距离
bool st[N];//存储状态
void add(int a, int b, int c)
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}
int dijkstra()
{
memset(dist, 0x3f, sizeof dist);//距离初始化为无穷大
dist[1] = 0;
priority_queue<PII, vector<PII>, greater<PII>> heap;//小根堆
heap.push({0, 1});//插入距离和节点编号
while (heap.size())
{
auto t = heap.top();//取距离源点最近的点
heap.pop();
int ver = t.second, distance = t.first;//ver:节点编号,distance:源点距离ver 的距离
if (st[ver]) continue;//如果距离已经确定,则跳过该点
st[ver] = true;
for (int i = h[ver]; i != -1; i = ne[i])//更新ver所指向的节点距离
{
int j = e[i];
if (dist[j] > dist[ver] + w[i])
{
dist[j] = dist[ver] + w[i];
heap.push({dist[j], j});//距离变小,则入堆
}
}
}
if (dist[n] == 0x3f3f3f3f) return -1;
return dist[n];
}
int main()
{
scanf("%d%d", &n, &m);
memset(h, -1, sizeof h);
while (m -- )
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c);
}
cout << dijkstra() << endl;
return 0;
}
//参考yxc
使用小根堆后,找到 t 的耗时从 O(n^2) 将为了 O(1)。每次更新 dist 后,需要向堆中插入更新的信息。所以更新dist的时间复杂度有 O(e) 变为了 O(e*logn)。总时间复杂度有 O(n^2) 变为了 O(n + e*longn)。适用于稀疏图。
总结
迪杰斯特拉算法适用于求正权有向图中,源点到其余各个节点的最短路径。注意:图中可以有环,但不能有负权边。
例如:如下图就不能使用迪杰斯特拉算法求节点 1 到其余各个节点的最短距离。
{:weith=150 height=150}
求个点赞~~
这个distance有啥用呢
让 heap 保证最小距离在上面
注释那里,应该是建立邻接表吧
每次看到你的题解都有一种醍醐灌顶的感觉
《将为》
迪杰斯特拉算法应该也能用于无向图中吧?
这个代码里的distance确实没用到
st[N]是怎么跟每个点对应上的
用堆存储距离和编号的时候,这两个的位置为什么改变就错了???
这堆得按照distance来排,得把distance写前面才行
因为pair在优先队列中的比较顺序是先比较前面那一个再后面的int,小根堆中设置较小的距离优先比较
这个时间复杂度是怎么分析的啊。前面那个n是哪里来的。
建立邻接表?
来了支持支持
想借楼问一下这里continue那句怎么理解 不是太懂
堆中可能有已经计算出最短距离的点,当这个点pop的时候,就不用做后续操作了。(st[这个点] 为真,说明他之前已经pop过了)
明白了 谢谢大佬
另外,如果边权恒为1,就不用加这一句
放入堆中的条件是距离变短了,一旦这个点被放入堆中,意味着后面再有线连到这个点的都比原来的长,不会再将此点进入堆中,那为什么还要st标记,(还有就是我删了就时间超限,求大佬帮我理一下思路)
同问
首先因为有重边存在,而且好像不止一次入队吧。
大佬厉害,两篇dijsktra清晰明了
这是稠密图还是稀疏图
稀疏图
怎么才能知道w[i]存的是哪个节点到哪个节点的距离?
可以循环输出e[N],ne[N],w[N]
赞