Classification of minimum spanning tree
🐖: The minimum spanning tree is generally used for undirected graphs, and directed graphs are rarely used.
Method selection:
Dense graph: using naive Prim algorithm
Sparse graph: using Kruskal algorithm
Heap optimized Prim is not commonly used!
Plain Prim
The general idea can be referred to Dijkstra algorithm , and pay attention to the similarities and differences
thinking
1. Initialize all distances to + ∞
dist[1] = 0, dist[i] = + ∞ (dist array represents the distance from the starting point to point I)
2.n iterations
for(i = 0; i < n; i ++ )
① Find the nearest point that is not in the set (s represents the point that is currently in the connected block) and assign it to t
② Use t to update the distance from other points to the set (note the difference from dijkstra algorithm)
③ Add t to the set st[t] = true
Note: distance to the set: the edge with the smallest length among the points connected to the interior of the set
Typical example - acwing 858 Prim algorithm for minimum spanning tree
#include <iostream> #include <cstring> #include <algorithm> using namespace std; const int N = 510,INF = 0x3f3f3f3f; int n,m; int g[N][N],dist[N]; bool st[N]; int prim() { memset(dist,0x3f,sizeof dist); int res = 0; //Sum of weights of edges in connected blocks for(int i = 0; i < n; i ++ ) { int t = -1; for(int j = 1; j <= n; j ++ ) if(!st[j] && (t == -1 || dist[t] > dist[j])) //Find the nearest point that is not in the set t = j; if(i && dist[t] == INF) return INF; //It is not the first point and is not connected. There is no minimum spanning tree if(i) res += dist[t]; for(int j = 1; j <= n; j ++ ) //Update the distance from the point to the set with t dist[j] = min(dist[j],g[t][j]); st[t] = true; //Add t to the set } return res; } int main() { cin >> n >> m; memset(g,0x3f,sizeof g); while(m -- ) { int a,b,c; cin >> a >> b >> c; g[a][b] = g[b][a] = min(c,g[a][b]); } int t = prim(); if(t == INF) puts("impossible"); else cout << t << endl; return 0; }
Note:
if(i) res += dist[t]; for(int j = 1; j <= n; j ++ ) //Update the distance from the point to the set with t dist[j] = min(dist[j],g[t][j]);
The order of the above two steps cannot be reversed!
Be sure to add the result first, and then update the other edges with t points. The reason is that there may be a self ring. If you update with t first, the self ring of point t may be updated to dist, but there can be no self ring in the spanning tree. It is wrong to add this to the result.
*The idea of heap optimized Prim algorithm is basically the same as that of heap optimized Dijkstra algorithm, which is very troublesome and can hardly be used. For ideas on heap optimization, please refer to my previous blog: Shortest path problem
Kruskal algorithm
Idea:
1) First, sort all edges from small to large by weight O(mlogm) (algorithm bottleneck, this step is the slowest step, but this is only the theoretical time complexity. In fact, in Kruskal algorithm, the constant of this step is very small and the performance effect is usually very good)
2) Enumerate each edge a,b and weight c from small to large
if(a,b not connected)
Add this edge to the collection
Note: Step 2 needs to use and search the collection. You can see my previous blog Joint search set Refer to the topic "number of connected blocks"“
advantage:
1) There is no need to store edges with complex data structures such as adjacency tables. Just open a structure;
2) Simple idea
Typical example acwing 859 Kruskal algorithm for minimum spanning tree
#include<iostream> #include<algorithm> using namespace std; const int N = 100010; int n,m; int p[N]; //Use a structure to store all edges struct Edge { int a,b,w; //Overload less than sign to facilitate sorting (sort by weight) bool operator< (const Edge &W)const { return w < W.w; } }edges[N]; int find(int x) { if(p[x] != x) p[x] = find(p[x]); return p[x]; } int main() { cin >> n >> m; for(int i = 0; i < m; i ++ ) { int a,b,w; cin >> a >> b >> w; edges[i] = {a,b,w}; } sort(edges,edges + m); for(int i = 1; i <= n; i ++ ) p[i] = i; //Initialize and query set int res = 0, cnt = 0; //res: the sum of the weights of all tree edges in the minimum spanning tree; //cnt: the number of current front edges (i.e. how many edges are currently added) //Enumerate all edges from small to large for(int i = 0; i < m; i ++ ) { int a = edges[i].a, b = edges[i].b, w = edges[i].w; a = find(a), b = find(b); if(a != b) //Judge whether a and b are connected { p[a] = b; //Merge two sets (that is, add this edge to the set) res += w; cnt ++; } } if(cnt < n - 1) puts("impossible"); //Disconnected else cout << res << endl; //The sum of the lengths of all tree edges return 0; }
This article mainly talks about Prim and Kruskal's two minimum spanning tree algorithms. You are welcome to criticize and correct!