一、先来比较一下常用的寻找最短路算法的时间复杂度(\(N\)为点数,\(M\)为边数)
1.未加优化的Dijkstra算法的时间复杂度为 \(O(N^2)\)
2.Floyd算法的时间复杂度为 \(O(N^3)\)
3.SPFA算法的时间复杂度为 \(O(KM)\) \(K\)为常数 \(\Longrightarrow\) \(K\) \(\begin{cases} \approx 2\quad &( 稀疏图 )\\ \\Large\ number\quad& ( 稠密图 ) \end{cases}\)
其实个人挺喜欢SPFA的,我觉得挺全能的,既能处理负权而且在 \(N\) 比较小时有着不错的时间复杂度。
二、Dijkstra堆优化
以Luogu P4779 为例(本题中的\(1\le n\le10^5\),\(1\le m\le 2\times 10^5\))

很显然\(O((10^5)^2)\)和\(O((10^5)^3)\)绝对是TLE的,所以朴素的Dijkstra和Floyd都不能解决此问题
SPFA也许可以?可以?可以?真的可以吗?

不可以!
这时候就需要在朴素Dijkstra上进行亿点点改进,将优先队列结合入Dijkstra中,也就是堆优化。
可以用C++自带的STL\(\longmapsto\)Priority_queue,优先队列的作用是维护队列中dis的值从小到大排列,这样在搜索时可以更快地找到起点到各点的最
小值,在松弛操作时减少可以push进队列的可能,从而减少时间复杂度。但是STL中的队列默认是大根堆,从小到大排列是小根堆,这里需要重置运算符。
#include<bits/stdc++.h>
#define INF 2147483647
using namespace std;
int n,m,s;
int cnt,head[200005];
int f[100005],vis[100005];
struct Edge{
int to,next,dis;
}edge[200005];
void Add_edge(int from,int to,int w)
{
edge[++cnt].dis=w;
edge[cnt].to=to;
edge[cnt].next=head[from];
head[from]=cnt;
}
struct node{
int dis,id;
bool operator <(const node &a)const{ return a.dis<dis; }//修改运算符,按dis从小到大
};
void Dijkstra()
{
priority_queue<node> q;
q.push(node{0,s});
for(int i=1;i<=n;i++) f[i]=INF; f[s]=0;
while(!q.empty())
{
node a=q.top(); q.pop();
int now=a.id;
if(vis[now]) continue;
int i=head[now]; vis[now]=1;
for(;i;i=edge[i].next)
{
int j=edge[i].to;
if(f[now]+edge[i].dis<f[j])
{
f[j]=f[now]+edge[i].dis;
q.push(node{f[j],j});
}
}
}
}
inline int read()
{
int ret=0; char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
return ret;
}
int main()
{
int u,v,w;
n=read(); m=read(); s=read();
for(int i=1;i<=m;i++)
{
u=read(); v=read(); w=read();
Add_edge(u,v,w);
}
Dijkstra();
for(int i=1;i<=n;i++) printf("%d ",f[i]);
}
时间复杂度为\(O((m+n)logm)\)

三、总结
即使堆优化的Dijkstra时间复杂度很棒,但是它依然不能处理负权!!!