什么是最小生成树?
首先,最小生成树一定数无向图,并且在不影响所有点都连通的情况下,所有边的权重加起来最小值是多少。
比如说:无向图abcp如下图所示,每条边权重也标记出来了。最小生成树就如右侧所示。
当然,不要1和2的边也可使所有点连通,但不是最小的。
Kruskal算法
用Kruskal生成最小树的思路可总体概括为:贪心算法+并查集的思路。
考察所有的边,从权重小的边到权重大的边依次考察(贪心),如果当前边不会形成环,就保留当前边,如果会形成环,就不要当前边。
仍然用上面abcp图来举例:
从小到大检查,权重1的边连接AC,不会形成环,保留。
权重2的边连接BC,不会形成环,保留。
权重3的边连接CP,不会形成环,保留。
权重100边连接AB,此时ABC形成了环,不要它,AP边也是如此,形成了最终的最小树。
那该如何检查环呢?此时就用到了并查集。不了解的建议先看下并查集介绍及其原理。
代码实现
通过小根堆将Edge的权重排序,小的在堆顶,而后弹出,看边的from和to节点在并查集中是否在一个集合。不在则union,在则说明有环。
public static class UnionFind {
HashMap<Node, Integer> sizeMap;
HashMap<Node, Node> parent;
public UnionFind() {
sizeMap = new HashMap<>();
parent = new HashMap<>();
}
public void makeSets(Collection<Node> nodes) {
sizeMap.clear();
;
parent.clear();
for (Node cur : nodes) {
sizeMap.put(cur, 1);
parent.put(cur, cur);
}
}
public Node findParent(Node cur) {
Stack<Node> stack = new Stack<>();
while (cur != parent.get(cur)) {
stack.add(cur);
cur = parent.get(cur);
}
while (!stack.isEmpty()) {
parent.put(stack.pop(), cur);
}
return cur;
}
public boolean isSameSet(Node a, Node b) {
return findParent(a) == findParent(b);
}
public void union(Node a, Node b) {
if (a == null || b == null) {
return;
}
Node aNode = findParent(a);
Node bNode = findParent(b);
if (aNode != bNode) {
int aSize = sizeMap.get(aNode);
int bSize = sizeMap.get(bNode);
if (aSize >= bSize) {
parent.put(bNode, aNode);
sizeMap.put(aNode, aSize + bSize);
sizeMap.remove(bNode);
} else {
parent.put(aNode, bNode);
sizeMap.put(aNode, aSize + bSize);
sizeMap.remove(aNode);
}
}
}
}
public static class MyComparator implements Comparator<Edge>{
@Override
public int compare(Edge o1, Edge o2) {
return o1.weight - o2.weight;
}
}
public static Set<Edge> kruskalMST(Graph graph) {
UnionFind uf = new UnionFind();
uf.makeSets(graph.nodes.values());
PriorityQueue<Edge> heap = new PriorityQueue(new MyComparator());
for (Edge edge : graph.edges){
heap.add(edge);
}
Set<Edge> result = new HashSet<>();
while (!heap.isEmpty()){
Edge edge = heap.poll();
while (!uf.isSameSet(edge.from,edge.to)){
uf.union(edge.from,edge.to);
result.add(edge);
}
}
return result;
}