甲最小生成树是一个连接的,无向图的生成树。它以最小的边缘总权重连接所有顶点。
例如,考虑上图。其最小生成树将是以下具有精确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–4
也具有权重5
。由于未形成任何周期,因此请将其包括在我们的MST中。
接下来,我们考虑3–5
具有权重的最小加权边6
。由于未形成任何周期,因此请将其包括在我们的MST中。
接下来,我们考虑0–1
具有权重的最小加权边7
。由于未形成任何周期,因此请将其包括在我们的MST中。
接下来,我们考虑最小的加权边1–4
也具有权重7
。由于未形成任何周期,因此请将其包括在我们的MST中。
接下来,我们考虑具有权重的最小加权边5–48
。但是在MST中包含此边缘将导致一个循环0—1—4—5—3—0
,因此我们将其丢弃。
接下来,我们考虑最小的加权边1-2也具有权重8
。但是在MST中包含此边缘将导致一个循环1—2—4—1
,因此我们将其丢弃。
接下来,我们考虑最小的加权边3-1也具有权重9
。但是在MST中包含此边缘将导致一个循环0—1—3—0
,因此我们将其丢弃。
最后,考虑下一个最小的加权边4–6
,也加权9
。由于未形成任何周期,因此请将其包括在我们的MST中。
现在已连接MST(包含V-1
边)。因此,我们丢弃所有剩余的边。
以下是根据Wikipedia编写的Kruskal算法的伪代码。它使用不相交的数据结构。
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
是图中顶点的总数。通过使用Union和Find操作的优化实现,可以将时间复杂度提高到O(N.log(N))。
参考:
1. https://en.wikipedia.org/wiki/Kruskal%27s_algorithm
2. http://lcm.csa.iisc.ernet.in/dsa/node184.html