1. kruskal:求加权连通图的最小生成树的算法
在含有n个顶点的连通图中选择n-1条边,构成一棵极小连通子图,并使该连通子图中n-1条边上权值之和达到最小,则称其为连通网的最小生成树
算法大体上的思路是根据给出的边集构成的连通图,能够选择其中的若干条生成一棵树,并且树的所有边的权重之和是最小的
需要经过下面的步骤:
① 根据给出的若干条边创建一个边集,因为需要选择权重最小的边那么需要对产生的边集进行排序
边的定义如下,需要对起始的边与结束的边使用泛型,所以需要在类的后面加上泛型的符号,由于涉及到边的排序,而边是一个对象所以需要实现类要实现Comparable接口并且规定接口的实体类这样在compareTo方法中传进去的实体类就是当前的实体类,在compareTo方法中自定义比较的规则,因为边涉及的无非是权重的问题,所以可以对权重这个属性进行排序
public class Edge<T> implements Comparable<Edge>{
private T start;
private T end;
private int distance;
public Edge(T start, T end, int distance){
this.start = start;
this.end = end;
this.distance = distance;
}
public T getStart() {
return start;
}
public void setStart(T start) {
this.start = start;
}
public T getEnd() {
return end;
}
public void setEnd(T end) {
this.end = end;
}
public int getDistance() {
return distance;
}
public void setDistance(int distance) {
this.distance = distance;
}
@Override
public int compareTo(Edge obj) {
int targetDis = obj.getDistance();
return this.distance > targetDis ? 1 : (this.distance == targetDis ? 0 : -1);
}
@Override
public String toString() {
return this.getStart() + "--->" + this.getEnd() + " : " + this.getDistance();
}
}
② 创建了边集之后依次从权重最小的边的顶点开始选择,判断当前加入的边是否会构成连通图,这里就需要使用到并查集这个数据结构,对于一条边有起点与终点我们使用先查询这两个节点的父节点,加入父节点相同那么加入的这条点肯定会构成连通图应该舍弃掉,假如不同那么可以加入当前的边此时不构成连通图,当所加的边的数目等于节点的个数减1的时候那么这个时候说明最小的生成树已经生成了,退出循环即可
并查集的代码:
import java.util.HashSet;
import java.util.Set;
public class UnionFind {
//并查集
public static UFNode find(UFNode x) {
UFNode p = x;
Set<UFNode> set = new HashSet<UFNode>();
while(p.parent != null){
set.add(p);
p = p.parent;
}
//把节点x上的所有节点都指向x的最根的那个节点
for(UFNode node : set){
node.parent = p;
}
return p;
}
public static void union(UFNode x, UFNode y) {
find(y).parent = find(x);
}
public static class UFNode{
UFNode parent;
}
}
Kruskal代码:
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import 十二章_图.UnionFind.UFNode;
public class Kruskal {
private List<Edge> edges;
private int n;
private Set<Edge> set = new HashSet<Edge>();
private Map map = new HashMap();
public Kruskal(List<Edge> edges, int n){
this.edges = edges;
this.n = n;
for(Edge edge : edges){
UFNode node = new UnionFind.UFNode();
map.put(edge.getStart(), new UnionFind.UFNode());
map.put(edge.getEnd(), new UnionFind.UFNode());
}
}
public static void main(String[] args) {
List<Edge> edges = build();
Kruskal list = new Kruskal(edges, 5);
for(Edge edge : list.getT()){
System.out.println(edge);
}
}
private Set<Edge> getT() {
buildMST();
return set;
}
private void buildMST() {
//因为实现了Comparable接口所以可以自定义排序规则
Collections.sort(edges);
for(Edge edge : edges){
if(!isOk(edge)) continue;
set.add(edge);
//当顶点数等于边数减1那么应该终止循环说明生成了最小的生成树了
if(set.size() == n - 1) return;
}
}
//使用并查集来实现功能
private boolean isOk(Edge edge) {
UnionFind.UFNode x = (UFNode) map.get(edge.getStart());
UnionFind.UFNode y = (UFNode) map.get(edge.getEnd());
if(UnionFind.find(x) != UnionFind.find(y)){
UnionFind.union(x, y);
return true;
}
return false;
}
@SuppressWarnings("unchecked")
private static List<Edge> build() {
List<Edge> list = new ArrayList<>();
list.add(new Edge<String>("C", "D", 1));
list.add(new Edge<String>("C", "A", 1));
list.add(new Edge<String>("C", "E", 8));
list.add(new Edge<String>("A", "B", 3));
list.add(new Edge<String>("D", "E", 3));
list.add(new Edge<String>("B", "C", 5));
list.add(new Edge<String>("B", "E", 6));
list.add(new Edge<String>("B", "D", 7));
list.add(new Edge<String>("A", "D", 2));
list.add(new Edge<String>("A", "E", 9));
return list;
}
}
重写Edge的toString方法,最后的代码运行的输出结果是:
C--->D : 1
A--->B : 3
C--->A : 1
D--->E : 3