Minimum spanning tree based on prim algorithm and Kruskal algorithm

Minimum spanning tree

Related concepts

  1. Connected graph : For graph G = {V, {E}} (V means vertex, E means edge). If any two vertices v, u have a path from v to u, then G is called a connected graph, and if the graph is a directed graph, it is called a strongly connected graph .

  2. Subgraph : For graph G1 = {V1,{E1}} and graph G = {V, {E}}, if V1 ⊆ V, E1 ⊆ E. Then call G1 a subgraph of G.

  3. Minimal connected subgraph : For a connected subgraph of graph G, when any edge (arc) in the subgraph is deleted, the subgraph will no longer be connected . It can be seen that the subgraph is similar to the structure of a tree, and each node will have only one in-degree and one out-degree. (Only n-1 edges)

  4. Spanning tree : Minimal connected subgraph (only n-1 edges) that includes all vertices of graph G, there may be more than one. Because each vertex has only one in-degree and out-degree, it is equivalent to the structure of a tree, so the subgraph can be regarded as a tree.

  5. Minimum spanning tree : the spanning tree with the smallest sum of the weights of the edges of the spanning tree.

(Note, for spanning trees, the above graph G should be more accurately described as a network, because one of the prerequisites for seeking the minimum spanning tree is that the edges have weights, and the concept of the network will be used below)

Guidelines for finding the minimum spanning tree

  1. The edge of the minimum spanning tree is the edge in the network G.
  2. There are only n-1 edges to connect the vertices in the minimum spanning tree.
  3. Instead of using edges that generate loops.

Three points are clear

  1. Using different graph traversal methods, the resulting spanning tree will be different.
  2. Starting from different vertices, the spanning tree obtained is different.
  3. There are n vertices and n-1 edges in the spanning tree.

aims

Among the multiple spanning trees of the network G, a weight and the smallest spanning tree is found, which is the minimum spanning tree.

  There are many algorithms for constructing a minimum spanning tree, let's introduce two algorithms that use greedy and dynamic programming ideas. Prim algorithm and Kruskal algorithm (Kruskal).

Primm Algorithm

  Prim algorithm, or DJP algorithm, adopts a greedy idea. Its main operation process is to select an edge with the smallest weight in each traversal. The specific process is as follows:

Basic idea of ​​Prim algorithm

  • Suppose the network G = {V, E}, T is the set of edges of the minimum spanning tree on G, and the set U is the subset of vertices of the edges on G on T, that is, the set of vertices of the minimum spanning tree. Initially T = {}, U = {u0}, u0 is the starting point of the spanning tree
  1. Select an edge (u0, v) with the smallest weight from all edges of the starting point u0, add this edge to T, and add vertex v to the set U.
  2. In each subsequent step, select an edge with the smallest weight from all edges (u, v) of u ∈ U, v ∈ VU, add its vertex v to the set U, and add the edge to the set T
  3. Repeat the process 2 until the set U=V.

From the above process, it can be seen that each step is the edge with the smallest weight. This is the greedy idea.

Icon

Prim

  Start from vertex 0, that is, U={v0} at this time, and then select the smallest weight of the associated edge as v5, add v5 to U, and add edge (v0, v5) to T. Next, in the edges (v0, v1) associated with vertices v0 and v5 in U, the smallest weight in (v5, v4) is the edge (v5, v4), so v4 is added to the set U, as shown in the figure. process.

Code

#include <iostream>
#include <cstring>
using namespace std;
int graph[2015][2015];		//使用邻接矩阵的方式保留图结构 
/**
 * 	获取当前边权重的最小值的顶点位置
 * 		minWeight:用来保存顶点的最小边的值
 * 		n:顶点的数量
 * 		v:顶点是否已经遍历
 */
int getMinWeight(int *minWeight, int n, int *v) {
    int min = 1 << 30;	//设置一个极大值
    int index =  0;
    
    for (int i = 1; i <= n; i++) {
        if (v[i] == 0 && minWeight[i] < min) {
            min = minWeight[i];
            index = i;
        }
    }
    return index;
}


int prim(int n, int m) {
    int sum = 0;
    int minWeight[n + 1];
    memset(minWeight, 127 / 3, sizeof(minWeight));		//给保存边的最小值的数组初始化一个极大值
    int v[n] = {0};		//创建v数组用来保存该节点是否被遍历过
    minWeight[1] = 0;		//确保在获取最小值的时候第一个顶点会被遍历到
    for (int i = 1; i <= n; i++) {
        int node = getMinWeight(minWeight, n, v);		//获取选中的节点 -- 第一次将获取第一个节点
        v[node] = 1;	//设置该节点为遍历过了
        sum += minWeight[node];
        for (int j = 1; j <= n; j++) {		//更新最小边数组
            if (v[j] == 0 && graph[node][j] < minWeight[j]) {
                minWeight[j] = graph[node][j];
            }
        }
        
    }
    return sum;
}


int main() {
    int n, m;
    cin >> n >> m;		//输入顶点树n和边m的数量
    memset(graph, 127/3, sizeof(graph));
    for (int i = 1; i <= m; i++) {
        int x, y, z;
        cin >> x >> y >> z;		//输入边的依赖关系即权值,(x, y)= z
        if (x == y) {		//可能有自环的情况出现,不保存这种情况的数据
            continue;
        }
        graph[x][y] = graph[y][x] = min(graph[x][y], z);	//可能会出现重边,即两条边的顶点一样,此时保存最小的边即可。
    }
    int sum = prim(n, m);		//获取最小生成树的总路径和
    cout << sum << endl;
}

Test case:

输入:
7 12
1 2 9
1 5 2
1 6 3
2 3 5
2 6 7
3 4 6
3 7 3
4 5 6
4 7 2
5 6 3
5 7 6
6 7 1
输出:
16

Kruskal Algorithm

Different from Prim's algorithm which uses greedy thinking, Kruskol's algorithm uses dynamic programming . The basic idea is as follows:

Basic idea of ​​Kruskal algorithm

  • Net G = {V, E},
  1. Construct a non-connected graph with only n vertices and no edges, that is, the number of edges=0. T = {V, {}} means that there is no edge non-connected graph, and each vertex in the graph forms a connected graph by itself.
  2. Select the edge with the smallest weight in the edge set E, and if the two vertices of the edge are located on different connected graphs, add the edge to the edge set of T. Otherwise, abandon the choice of the next edge with the least cost (so not always choose the edge with the smallest weight, which is different from greedy ).
  3. Repeat the 2 process, knowing that all the vertices are on the same connected component, this time constitutes a minimum spanning tree.

Icon

Kruskal

  As shown in the above figure (b), first create a set T= {V,{}} without edges. Then select the edge with the smallest weight (v0, v5) value of 10, and add this edge to T. Next, select the smallest weight edge (v2, v3) value of 12, and add this edge to T. Repeat the above process as shown in the figure until all the vertices are on the same connected graph. At this time, the tree constructed is the lowest spanning tree.

Code example

#include <iostream>
#include <algorithm>
using namespace std;

struct _node {
    int x, y;
    int weight;
    bool operator(_node a) {
        return weight < a.weight
    }
}edge[1024];
int node[1024], v[1024];
/**
 * 使用并查集的方式查找没有相邻的两个顶点
 */
int findIndex(int x) {
    if (x == v[x]) {
        return x;
    }
    return v[x] = findIndex(v[x]);
}

int main() {
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {	//初始化并查集数组
        v[i] = i;
    }
    for (int i = 1; i <= m; i++) {
        cin >> edge[i].x >> edge[i].y >> edge[i].weight;
    }
    sort(e + 1, e + m + 1);		//由于是每次取最小的边,先将边集合进行排序
    sum += 0;
    for (int i = 1; i <= m; i++) {
        //获取边中两个节点在并查集中的情况,
        int node1 = findIndex(edge[i].x);
        int node2 = findIndex(edge[i].y);
        if (node1 != node2) {	//如果不相等则说明两个顶点不连通,则取这条边
            sum += edge[i].weight;
            v[node1] = node2;	//设置两个顶点连通
        }
    }
    cout << sum;
}


The test case is as above.

  Since the Kruskal algorithm judges whether the two vertices in the graph are connected or not based on the way of union search, you can simulate it manually according to the code to see the effect. Or use the search method to determine whether two vertices are connected.

Guess you like

Origin blog.csdn.net/weixin_44184990/article/details/108675465