最小生成树(Kruskal、Prim)(Java)

无向图用的是《算法导论》书上的,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;
	}
	
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值