一、先來比較一下常用的尋找最短路算法的時間復雜度(\(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時間復雜度很棒,但是它依然不能處理負權!!!