无向图用的是《算法导论》书上的,Kruskal、Prim算法都是贪心的。
最小生成树算法的核心是找到安全边。解决的问题是连接n个点所需的最小边长。
Kruskal算法时间复杂度为O(ElgV),Prim算法时间复杂度取决于最小优先队列。
我用的是Java中自带的优先队列,使用时要注意,当优先队列改变时(删除等操作才会刷新排序,应该是源码中删除等操作的代码中实现了重新排列)
两个算法的代码如下:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Set;
public class KruskalAndPrimMSTtest {
public static void main(String[] args) {
PrimAndKruskalMSTtest();
}
public static void PrimAndKruskalMSTtest() {
Map<V,List<V>> G = new HashMap<>();
V aV = new V('a');
V bV = new V('b');
V cV = new V('c');
V dV = new V('d');
V eV = new V('e');
V fV = new V('f');
V gV = new V('g');
V hV = new V('h');
V iV = new V('i');
List<V> aN = new LinkedList<>(); aN.add(bV);aN.add(hV);
List<V> bN = new LinkedList<>(); bN.add(aV);bN.add(cV);bN.add(hV);
List<V> cN = new LinkedList<>(); cN.add(bV);cN.add(dV);cN.add(fV);cN.add(iV);
List<V> dN = new LinkedList<>(); dN.add(cV);dN.add(eV);dN.add(fV);
List<V> eN = new LinkedList<>(); eN.add(dV);eN.add(fV);
List<V> fN = new LinkedList<>(); fN.add(cV);fN.add(dV);fN.add(eV);fN.add(gV);
List<V> gN = new LinkedList<>(); gN.add(fV);gN.add(hV);gN.add(iV);
List<V> hN = new LinkedList<>(); hN.add(aV);hN.add(bV);hN.add(gV);hN.add(iV);
List<V> iN = new LinkedList<>(); iN.add(cV);iN.add(gV);iN.add(hV);
G.put(aV, aN);
G.put(bV, bN);
G.put(cV, cN);
G.put(dV, dN);
G.put(eV, eN);
G.put(fV, fN);
G.put(gV, gN);
G.put(hV, hN);
G.put(iV, iN);
List<Edge> alledges = new ArrayList<>();
alledges.add(new Edge(aV, bV, 4));
alledges.add(new Edge(aV, hV, 8));
alledges.add(new Edge(bV, cV, 8));
alledges.add(new Edge(bV, hV, 11));
alledges.add(new Edge(cV, dV, 7));
alledges.add(new Edge(cV, fV, 4));
alledges.add(new Edge(cV, iV, 2));
alledges.add(new Edge(dV, eV, 9));
alledges.add(new Edge(dV, fV, 14));
alledges.add(new Edge(eV, fV, 10));
alledges.add(new Edge(fV, gV, 2));
alledges.add(new Edge(gV, iV, 6));
alledges.add(new Edge(gV, hV, 1));
alledges.add(new Edge(iV, hV, 7));
ArrayList<Edge> A = KruskalMST(G, alledges);//不需要起始点,根据边的长短
System.out.println("经过KruskalMST算法后,得到的列表A是:"+A);
PrimMST(G, alledges, aV);
}
public static ArrayList<Edge> KruskalMST(Map<V, List<V>> G, List<Edge> alledges){
ArrayList<Edge> A = new ArrayList<>();
ArrayList<Set<V>> allset = new ArrayList<>();//储存所有树(集合)的大集合
for(V k : G.keySet()) {//初始时,每个结点都是一颗树(集合)
Set<V> kset = new HashSet<>();//用hashset保持唯一性
kset.add(k);
allset.add(kset);
}
alledges.sort((e1,e2)->{return e1.w - e2.w;});//按权重排序边
for(Edge edge : alledges) {
Set<V> contains_s = findSet(allset,edge.s);
Set<V> contains_d = findSet(allset,edge.d);
if(!contains_s.equals(contains_d)) {
Set<V> merge = new HashSet<>();
merge.addAll(contains_d);
merge.addAll(contains_s);
allset.remove(contains_d);
allset.remove(contains_s);
allset.add(merge);
A.add(edge);
}
}
return A;
}
public static Set<V> findSet(ArrayList<Set<V>> allset, V v) {
for(Set<V> iset : allset) {
if(iset.contains(v))
return iset;
}
return null;
}
public static void PrimMST(Map<V, List<V>> G, List<Edge> alledges,V r) {
r.key = 0;
int getw = Integer.MAX_VALUE;
PriorityQueue<V> Q = new PriorityQueue<>((v1,v2)-> {return v1.key-v2.key;});
Q.addAll(G.keySet());//一次把结点全加入到优先队列中
while(!Q.isEmpty()) {
V u = Q.poll();//获取当前key值最小的结点
for (V v : G.get(u)) {//获取u的所有邻点,遍历赋值,下次取出的u为当前被发现的点中key值最小的
for( Edge uv : alledges)
if( (uv.s.equals(u) && uv.d.equals(v)) || (uv.d.equals(u) && uv.s.equals(v)))
getw=uv.w;//取出u与v的这条边的权重
if(Q.contains(v) && getw<v.key) {
v.p = u;
v.key = getw;
Q.remove(v);//优先队列要改变才能重新排列
Q.add(v);
}
}
}
for(V k : G.keySet())
System.out.println("经过PrimMST算法后," + k + "的父节点和key值为" + k.p + " " + k.key);
}
}
//结点类
class V{
char name;
int key;//prim算法要用的
V p;//prim算法要用的
public V(char name) {
this.name = name;
key = Integer.MAX_VALUE;
p = null;
}
public int hashCode() {//hashmap与hashset必须重写
return name;
}
public boolean equals(Object o) {//hashmap与hashset必须重写
if(o == null || !(o instanceof V) )
return false;
if(o == this)
return true;
return name == ((V)o).name;
}
public String toString() {
return "" + name;
}
}
//边类
class Edge {
V s;//s与d代表边连接的2个点
V d;
int w;//边的权重
public Edge(V s,V d,int w) {
this.w = w;
this.s = s;
this.d = d;
}
public String toString() {
return s + "-" + d + " " + w;
}
}