时间复杂度对比
Dijkstra: O(N^2);
Dijkstra + 优先队列优化 : O (2 * E + V * logV)
SPFA: O(k * E),k 为每个节点进入队列的次数,一般小于2,最坏 O(V * E);
BellmanFord:O(V * E), 可检测负权。
Floyd:O(n^3),j计算每个节点到节点的最短路。
结论
1)单源最短路 ,没有权值为负的边 用Dijkstra(可用堆优化);
2)求每个顶点到每个顶点的最短路,用Floyd
3)权值有负时,但是没有负圈,用SPFA,但是SPFA能检测负圈,但是不能输出负圈,
4)权值有负时,可能有负圈,用BellmanFord,能检测并输出负圈。
5)SPFA检测负环,当存在一个点入队大于V次时,有负环。
单源最短路(边不能为负)
伪代码
Dijkstra(G, d[], s){
初始化
for(循环n次){
u = 使d[u]最小的还未别访问的顶点的标号;
记 u 已经被访问;
for(从u出发能到达的所有顶点){
if(v未被访问 && 以u中介点使s到顶点v的最短距离d[v]更优){
优化d[v];
}
}
}
}
1)邻接矩阵
const int MAXV = 1000;
const int INF = 0x3f3f3f3f;
int n, G[MAXV][MAXV];
int d[MAXV];
bool vis[MAXV] = {false};
void Dijkstra(int s){
fill(d, d + MAXV, INF);
d[s] = 0;
for(int i = 0; i < n; i++){
int u = -1, MIN = INF;
for(int j = 0; j < n; j ++){
if(vis[j] == false && d[j] < MIN){
u = j;
MIN = d[j];
}
}
if(u == -1) return ;
vis[u] = true;
for(int v = 0; v < n; v++){
if(vis[v] == false && G[u][v] != INF && d[u] + G[u][v] < d[v]){
d[v] = d[u] + G[u][v];
}
}
}
}
2)邻接表
struct Node{
int v, dis;
};
vector<Node> Adj[MAXV];
int n;
int d[MAXV];
bool vis[MAXV] = {false};
void Dijkstra(int s){
fill(d, d + MAXV, INF);
d[s] = 0;
for(int i = 0; i < n; i ++){
int u = -1, MIN = INF;
for(int j = 0; j < n; j++){
if(vis[j] == false && d[j] < MIN){
u = j;
MIN = d[j];
}
}
if(u == -1) return ;
vis[u] = true;
for(int j = 0; j < Adj[u].size(); j ++){
int v = Adj[u][j].v;
if(vis[v] == false && d[u] + Adj[u][j].dis < d[v]){
d[v] = d[u] + Adj[u][j].dis;
}
}
}
}
3)优先队列优化
#include<iostream>
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, ll> pii; //first 是 点, second 是距离
const int N = 1e5 + 10;
const ll INF = (ll) 1e16;
vector<pii> V[N];
int n, m;
bool vis[N];
ll dis[N];
struct Node{
int id;
ll d;
Node(){}
Node(int id, ll d): id(id), d(d){}
bool operator < (const Node &A)const{
return d > A.d;
}
};
void Dijkstra(int st){
for(int i = 1; i <= n; i++){
vis[i] = 0;
dis[i] = INF;
}
dis[st] = 0;
priority_queue<Node> Q;
Q.push(Node(st, 0)); //先把第一个点入队
while(!Q.empty()){
Node nd = Q.top(); Q.pop(); //每次都用优先队列队首(找到边最短的点)以这个点更新
if(vis[nd.id]) continue; //如果已经用这个点更新过就跳过
vis[nd.id] = true; //标记用这个点更新
for(int i = 0; i < V[nd.id].size(); i++){ //以这个点更新
int k = V[nd.id][i].first; //与这个点相连的点
int len = V[nd.id][i].second; //距离
if(nd.d + len < dis[k] && !vis[k]){ //距离变小,且没有以这个点更新过
dis[k] = nd.d + len; //更新距离
Q.push(Node(k, dis[k])); //入队
}
}
}
}
int main (){
int x, y, z, st, ed, cas = 0;
scanf("%d", &cas);
while(cas--){
scanf("%d%d%d", &n, &m, &st);
for(int i = 0; i <= n; i++) V[i].clear();
while(m --){
scanf("%d%d%d", &x, &y, &z);
V[x].push_back(make_pair(y, z));
//V[y].push_back(make_pair(x, z));
}
Dijkstra(st);
for(int i = 1; i <= n; i++)
if(i == 1) printf("%d", dis[i]);
else printf(" %d", dis[i]);
}
return 0;
}