最小生成树的Kruskal算法

给定一个无向,连通和加权的图,使用Kruskal算法从中构造出最小生成树。

最小生成树是一个连接的,无向图的生成树。它以最小的边缘总权重连接所有顶点。

克鲁斯卡尔算法-步骤1

例如,考虑上图。其最小生成树将是以下具有精确N-1边的树,其中N是图中的顶点总数,并且边的权重之和应尽可能小:

克鲁斯卡尔算法

先决条件:

图中循环检测的联合查找算法

 

 
我们可以使用Kruskal的最小生成树算法(一种贪婪算法)为连接的加权图找到最小生成树。Kruskal算法的工作原理是,从给定图中找到覆盖图中存在的每个顶点的边的子集,以使它们形成一棵树(称为MST),并且边的权重之和应尽可能小。

让给G = (V, E)定的图。最初,我们的MST仅包含给定图的顶点,没有边。换句话说,最初,MST具有V 连接的组件,每个顶点充当一个连接的组件。目标是向我们的MST添加最小权重边,以使我们剩下一个包含所有图顶点的单个连接组件。以下是完整的算法:

G按权重增加的顺序对 图中的所有边缘进行排序;
重复V-1次// //因为MST包含V-1
{
    从图形中选择权重最小的下一条边G
 
    如果(通过在MST中添加边没有形成循环,即,边在MST中连接两个
            不同的连接组件)
        将边添加到MST;
}

让我们以上图为例对此进行说明。最初,我们的MST仅由给定图的顶点组成,没有边。我们首先考虑0–3具有权重的最小加权边5。由于未形成任何周期,因此请将其包括在我们的MST中。

克鲁斯卡尔算法-第2步

接下来,我们考虑最小的加权边2–4也具有权重5。由于未形成任何周期,因此请将其包括在我们的MST中。

克鲁斯卡尔算法-步骤3

接下来,我们考虑3–5具有权重的最小加权边6。由于未形成任何周期,因此请将其包括在我们的MST中。

克鲁斯卡尔算法-步骤4

接下来,我们考虑0–1具有权重的最小加权边7。由于未形成任何周期,因此请将其包括在我们的MST中。

克鲁斯卡尔算法-步骤5

接下来,我们考虑最小的加权边1–4也具有权重7。由于未形成任何周期,因此请将其包括在我们的MST中。

克鲁斯卡尔算法-步骤6

接下来,我们考虑具有权重的最小加权边5–48。但是在MST中包含此边缘将导致一个循环0—1—4—5—3—0,因此我们将其丢弃。

克鲁斯卡尔算法-步骤7

接下来,我们考虑最小的加权边1-2也具有权重8。但是在MST中包含此边缘将导致一个循环1—2—4—1,因此我们将其丢弃。

克鲁斯卡尔算法–步骤8

接下来,我们考虑最小的加权边3-1也具有权重9。但是在MST中包含此边缘将导致一个循环0—1—3—0,因此我们将其丢弃。

克鲁斯卡尔算法-步骤9

最后,考虑下一个最小的加权边4–6,也加权9。由于未形成任何周期,因此请将其包括在我们的MST中。
克鲁斯卡尔算法-步骤10

现在已连接MST(包含V-1边)。因此,我们丢弃所有剩余的边

克鲁斯卡尔算法-步骤11

以下是根据Wikipedia编写的Kruskal算法的伪代码。它使用不相交的数据结构。

KRUSKAL(图G)
 
MST = {}属于GV的
 
每个顶点v: GE中每个(u,v)的权重(u,v)的
    MAKE-SET(v)
 
,按权重(u,v)排序,递增:
    如果FIND-SET(u)!= FIND-SET(v):
        添加{(u,v)}以设置MST
        UNION(u,v)
 
返回MST

请注意,如果该图未连接,则Kruskal算法会找到最小生成树,即图的每个已连接组件的最小生成树。

该算法Java实现如下:

 

 

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import java.util.*;
 
// A class to store a graph edge
class Edge
{
    int src, dest, weight;
 
    public Edge(int src, int dest, int weight)
    {
        this.src = src;
        this.dest = dest;
        this.weight = weight;
    }
 
    @Override
    public String toString() {
        return "(" + src + ", " + dest + ", " + weight + ")";
    }
}
 
// A class to represent a disjoint set
class DisjointSet
{
    Map<Integer, Integer> parent = new HashMap<>();
 
    // perform MakeSet operation
    public void makeSet(int N)
    {
        // create `N` disjoint sets (one for each vertex)
        for (int i = 0; i < N; i++) {
            parent.put(i, i);
        }
    }
 
    // Find the root of the set in which element `k` belongs
    private int Find(int k)
    {
        // if `k` is root
        if (parent.get(k) == k) {
            return k;
        }
 
        // recur for the parent until we find the root
        return Find(parent.get(k));
    }
 
    // Perform Union of two subsets
    private void Union(int a, int b)
    {
        // find the root of the sets in which elements
        // `x` and `y` belongs
        int x = Find(a);
        int y = Find(b);
 
        parent.put(x, y);
    }
 
    // Function to construct MST using Kruskal’s algorithm
    public static List<Edge> kruskalAlgo(List<Edge> edges, int N)
    {
        // stores the edges present in MST
        List<Edge> MST = new ArrayList();
 
        // Initialize `DisjointSet` class.
        // create a singleton set for each element of the universe.
        DisjointSet ds = new DisjointSet();
        ds.makeSet(N);
 
        int index = 0;
 
        // sort edges by increasing weight
        Collections.sort(edges, Comparator.comparingInt(e -> e.weight));
 
        // MST contains exactly `V-1` edges
        while (MST.size() != N - 1)
        {
            // consider the next edge with minimum weight from the graph
            Edge next_edge = edges.get(index++);
 
            // find the root of the sets to which two endpoints
            // vertices of the next edge belongs
            int x = ds.Find(next_edge.src);
            int y = ds.Find(next_edge.dest);
 
            // if both endpoints have different parents, they belong to
            // different connected components and can be included in MST
            if (x != y)
            {
                MST.add(next_edge);
                ds.Union(x, y);
            }
        }
        return MST;
    }
}
 
class Main
{
    public static void main(String[] args)
    {
        // `(u, v, w)` triplet represent undirected edge from
        // vertex `u` to vertex `v` having weight `w`
        List<Edge> edges = Arrays.asList(
                new Edge(0, 1, 7), new Edge(1, 2, 8),
                new Edge(0, 3, 5), new Edge(1, 3, 9),
                new Edge(1, 4, 7), new Edge(2, 4, 5),
                new Edge(3, 4, 15), new Edge(3, 5, 6),
                new Edge(4, 5, 8), new Edge(4, 6, 9),
                new Edge(5, 6, 11)
        );
 
        // total number of nodes in the graph
        final int N = 7;
 
        // construct graph
        List<Edge> e = DisjointSet.kruskalAlgo(edges, N);
        System.out.println(e);
    }
}

 

 

输出:

[[0,3,5),(2,4,5),(3,5,6),(0,1,7),(1,4,7),(4,6,9) ]

 

 

 

上述解决方案的时间复杂度为O(N 2),其中N是图中顶点的总数。通过使用UnionFind操作优化实现,可以将时间复杂度提高到O(N.log(N))

 
参考:

1. https://en.wikipedia.org/wiki/Kruskal%27s_algorithm

2. http://lcm.csa.iisc.ernet.in/dsa/node184.html

 

 

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值