可以求起点 S 到其他点的最短路径,时间复杂度为 O(n2)
例: 找 1 到 n 的最短路径,如果不存在输出 −1
#include <cstring> #include <iostream> #include <algorithm> using namespace std; const int INF = 5e6; const int N = 510; int n, m; int g[N][N]; int dist[N]; bool st[N]; int dijkstra(int s) { for(int i=1; i<=n; ++i) dist[i] = INF; dist[s] = 0; for (int i = 0; i < n - 1; i ++ ) // 对于n个点的图,最多做n-1次更新 { int t = -1; // 找一个 dist[t]是全局最小的dist, 且没有被标记过 for (int j = 1; j <= n; j ++ ) if (!st[j] && (t == -1 || dist[t] > dist[j])) t = j; for (int j = 1; j <= n; j ++ ) dist[j] = min(dist[j], dist[t] + g[t][j]); st[t] = true; } if (dist[n] == INF) return -1; return dist[n]; } int main() { scanf("%d%d", &n, &m); //n个点, m条边 for(int i=1; i<=n; ++i) { for(int j=1; j<=n; ++j) { if(i==j) g[i][j]=0; else g[i][j] = INF; } } while (m -- ) { int a, b, c; scanf("%d%d%d", &a, &b, &c); g[a][b] = min(g[a][b], c); //防止有重边 } printf("%d\n", dijkstra(1)); return 0; }
时间复杂度不稳定,为 O(km), k 为常数。 最坏情况(网格图):O(n∗m) 。
void spfa() { for(int i=1; i<=52; ++i) { dist[i] = INF; vis[i] = 0; } queue<int> q; int x = 52; q.push(x); vis[x] = 1; dist[52] = 0; while(!q.empty()) { x = q.front(); q.pop(); vis[x] = 0; for(int i=head[x]; i!=-1; i=edge[i].nxt) { int v = edge[i].to; int w = edge[i].val; if(dist[v] > dist[x]+w) { dist[v] = dist[x]+w; if(!vis[v]){ mark[v]++; q.push(v); vis[v] = 1; } } } } }
因为在 n 个点的图中,一个点最多被更新 n−1 次。 所以当一个点的更新次数超过 n−1 时,说明存在负环。
在有向无环图中(DAG)中,拓扑排序可以 O(n) 的复杂度内访问所有的节点。
void Toporder() { for(int i=1; i<=n; ++i) { if(!indegree[i]) { cost[i] = 100; ans += cost[i]; que.push(i); } } while(!que.empty()) { int x = que.front(); que.pop(); for(int i=0; i<mp[x].size(); ++i) { int y = mp[x][i]; cost[y] = cost[x] + 1; indegree[y]--; cnt--; //总度数减一,若队列为空时,总度数不为0,则表示图中有环 if(!indegree[y]) { ans += cost[y]; que.push(y); } } } }
普通版: 每次查找根结点的时间复杂度最坏都是 O(n)
int Find(int x) { while(x!=bin[x]) x=bin[x]; return x; }
非递归版路径压缩:
int Find(int x) { // 非递归版的路径压缩 int r = x; while(r != bin[r]) r = bin[r]; while(x != r) { int t = bin[x]; bin[x] = r; x = t; } return x; }
递归版路径压缩:
int Find(int x) { if(x==bin[x]) return x; return bin[x] = Find(bin[x]); }
void merge(int x, int y) { int fx = Find(x); int fy = Find(y); if(fx!=fy) bin[fy] = fx; }
时间复杂度 O(n2)
#include <bits/stdc++.h> using namespace std; const int maxn = 1e3+5; const int INF = 1e9+10; int n,mp[maxn][maxn], ans=0, dist[maxn], vis[maxn]; void prim() { for(int i=1; i<=n; ++i) { dist[i] = mp[1][i]; vis[i] = 0; } dist[1] = 0; vis[1] = 1; for(int i=1; i<=n-1; ++i) { int t = -1; for(int j=2; j<=n; ++j) { if(!vis[j] && (t==-1 || dist[t]>dist[j])) t=j; } ans += dist[t]; //ans表示最小生成树的边权值之和 vis[t] = 1; for(int j=1; j<=n; ++j) { if(dist[j] > mp[t][j]) dist[j] = mp[t][j]; } } printf("%d\n", ans); } int main() { int m; scanf("%d%d", &n, &m); for(int i=1; i<=n; ++i) { for(int j=1; j<=n; ++j) { if(i==j) mp[i][j]=0; else mp[i][j] = INF; } } for(int i=1; i<=m; ++i) { int x, y, z; scanf("%d%d%d", &x, &y, &z); mp[x][y] = z; mp[y][x] = z; } prim(); return 0; }