Summary of typical minimum spanning tree algorithms~

Posted by schme16 on Tue, 18 Jan 2022 17:59:39 +0100

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!

Topics: Algorithm data structure Graph Theory kruskal Prim