Prim算法
Prim算法,每一步都会为一颗生长中的树添加一条边。一开始这棵树只有一个顶点,然后哦会向它添加V-1条边,每次总是将下一条连接树的顶点与不在树中且权重最小的边加入树中
实现
最小生成树的延迟实现
- 通过一个队列保存最小生成树的边(横切边) Queue mst
- 一个最小优先队列保存所有的边 MinPQ pq 通过删除最小边来得到新的最小生成树的边,通过基于堆的最小优先队列来实现
- 一个数组来保存所有的顶点(用于标记顶点是否被访问,一条边有两个顶点,两个顶点均已访问即说明已经添加,该边失效)
实现: 首先通过visit()方法将一个顶点添加进树中,将它标记为已访问,然后将该顶点所有与它关联的边加入优先队列,以保证队列含有所有连接树顶点和非树顶点的边
代码内循环实现: 我们从优先队列取出一条边并将它添加到树中,然后把这条边的另一个顶点也添加到树中,然后用新顶点作为参数调用visit()方法来更新横切边的结合,即把与新顶点相关联的边添加到优先队列中,再从优先队列中取出权重最小并未失效的边,再得到新顶点循环调用
public class LazyPrimMST {
public boolean[] marked; //最小生成树的顶点
public Queue<Edge> mst; //最小生成树的边
public MinPQ<Edge> pq; //横切边 (包括失效的边)
/**
* 将v个顶点和v-1条边加入最小生成树 无向图有v个顶点,v个顶点生成一颗树
* @param G
*/
public LazyPrimMST(EdgeWeightGraph G) {
pq = new MinPQ<Edge>();
mst = new LinkedList<>();
marked = new boolean[G.V()];
visit(G, 0);
while (!pq.isEmpty()) {
Edge e = pq.delMin();
System.out.println("删除最小节点:"+e);
int v = e.either(),w = e.other(v);
if(marked[v]&&marked[w])
continue; //跳过失效边
System.out.println("树添加边:"+e);
mst.add(e); //将边添加到树中
System.out.println("------------------------------------");
if(!marked[v]) //将新边另一顶点及所关联的边加入树中
visit(G,v);
if(!marked[w])
visit(G,w);
}
}
/**
* @param g
* @param i
*/
private void visit(EdgeWeightGraph g, int v) {
//标记顶点v为已访问并将所有连接v和未被标记顶点的边加入优先队列
marked[v] = true;
for(Edge e : g.adj(v)) {
if(!marked[e.other(v)])
System.out.println("添加节点"+ e);
pq.insert(e);
}
}
public Iterable<Edge> edges(){
return mst;
}
}
带权重的边的数据类型Edge
public class Edge implements Comparable<Edge>{
public final int v;
public final int w;
public final double weight;
public Edge(int v,int w,double weight) {
this.v = v;
this.w = w;
this.weight = weight;
}
public int either() {
return v;
}
public int other(int vertex) {
if(vertex==v)
return w;
else if(vertex==w)
return v;
else
throw new RuntimeException("Inconsistent Edge");
}
public double weight() {
return weight;
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return String.format("%d-%d %.2f", v,w,weight);
}
/* (non-Javadoc)
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
@Override
public int compareTo(Edge o) { //重写ComparaTo方法,使用权重来比较排序
// TODO Auto-generated method stub
if(this.weight()<o.weight()) return -1;
else if(this.weight()>o.weight()) return 1;
else return 0;
}
}
加权无向图的数据类型
public class EdgeWeightGraph {
private final int V;
private int E; //边总数
private Bag<Edge>[] adj; //邻接表
public EdgeWeightGraph(int v) {
this.V = v;
this.E = 0;
adj = new Bag[V];
for(int i = 0;i<V;i++) {
adj[i] = new Bag<Edge>();
}
}
public int V() {
return V;
}
public int E() {
return E;
}
public void addEdge(Edge e) {
int v = e.either();
int w = e.other(v);
adj[v].add(e);
adj[w].add(e);
E++;
}
public Iterable<Edge> adj(int v){
return adj[v];
}
public Iterable<Edge> edges(){ //返回所有的边
Stack<Edge> s = new Stack<Edge>();
for(int i = 0;i<V;i++) {
for (Edge edge : adj(i)) {
s.push(edge);
}
}
return s;
}
}
最小优先队列
public class MinPQ<Key extends Comparable<Key>> {
private Key[] pq;
private int N = 0;
public MinPQ(int initCapacity) {
pq = (Key[]) new Comparable[initCapacity + 1];
}
public MinPQ() {
this(1);
}
public void insert(Key key) {
if (N == pq.length - 1) {
resize(2 * pq.length);
}
N++;
pq[N] = key;
swim(N);
}
/**
* @param i
*/
private void resize(int i) {
Key[] temp = (Key[]) new Comparable[i];
for (int j = 0; j < pq.length; j++) {
temp[j] = pq[j];
}
pq = temp;
}
public Key delMin() {
exch(pq, 1, N);
Key min = pq[N];
pq[N] = null;
N--;
skin(1);
return min;
}
private void swim(int i) {
while (i > 1 && greater(i / 2, i)) {
exch(i, i / 2);
i = i / 2;
}
}
private void exch(int i, int j) {
Key temp = pq[i];
pq[i] = pq[j];
pq[j] = temp;
}
/**
* @param i
*/
private void skin(int i) {
while (2 * i <= N) {
int k = 2 * i;
if (k < N && greater(k, k + 1)) { // 比较右节点小 取右节点
k++;
}
if (greater(k, i)) { // 如果孩子节点大 不需要调整了
break;
}
exch(pq, i, k);
i = k;
}
}
private boolean greater(int i, int k) {
return pq[i].compareTo(pq[k]) > 0;
}
/**
* @param k
* @param i
*/
private static void exch(Comparable[] v, int k, int i) {
Comparable temp = v[i];
v[i] = v[k];
v[k] = temp;
}
public static void show(Comparable[] a) {
for (int i = 1; i < a.length; i++) {
System.out.print(a[i] + " ");
}
System.out.println();
}
private boolean less(int i, int j) {
return pq[i].compareTo(pq[j]) < 0;
}
/**
* @param i
* @param k
* @return
*/
private static boolean greater(Comparable i, Comparable k) {
// TODO Auto-generated method stub
System.out.println(i + " " + k);
return i.compareTo(k) > 0;
}
public int size() {
return N;
}
public boolean isEmpty() {
if (N == 0) {
return true;
}
return false;
}
}
测试
public class Test {
public static void main(String[] args) {
EdgeWeightGraph graph = new EdgeWeightGraph(8);
graph.addEdge(new Edge(4, 5, 0.35));
graph.addEdge(new Edge(4, 7, 0.37));
graph.addEdge(new Edge(5, 7, 0.28));
graph.addEdge(new Edge(0, 7, 0.16));
graph.addEdge(new Edge(1, 5, 0.32));
graph.addEdge(new Edge(0, 4, 0.38));
graph.addEdge(new Edge(2, 3, 0.17));
graph.addEdge(new Edge(1, 7, 0.19));
graph.addEdge(new Edge(0, 2, 0.26));
graph.addEdge(new Edge(1, 2, 0.36));
graph.addEdge(new Edge(1, 3, 0.29));
graph.addEdge(new Edge(2, 7, 0.34));
graph.addEdge(new Edge(6, 2, 0.40));
graph.addEdge(new Edge(3, 6, 0.52));
graph.addEdge(new Edge(6, 0, 0.58));
graph.addEdge(new Edge(6, 4, 0.93));
LazyPrimMST lpm = new LazyPrimMST(graph);
Queue<Edge> q = new LinkedList<Edge>();
q = (Queue<Edge>) lpm.edges();
System.out.println("最小生成树:");
for (Edge edge : q) {
System.out.println(edge);
}
}
}
运行结果:
添加节点6-0 0.58
添加节点0-2 0.26
添加节点0-4 0.38
添加节点0-7 0.16
删除最小节点:0-7 0.16
树添加边:0-7 0.16
------------------------------------
添加节点2-7 0.34
添加节点1-7 0.19
添加节点5-7 0.28
添加节点4-7 0.37
删除最小节点:0-7 0.16
删除最小节点:1-7 0.19
树添加边:1-7 0.19
------------------------------------
添加节点1-3 0.29
添加节点1-2 0.36
添加节点1-5 0.32
删除最小节点:1-7 0.19
删除最小节点:0-2 0.26
树添加边:0-2 0.26
------------------------------------
添加节点6-2 0.40
添加节点2-3 0.17
删除最小节点:2-3 0.17
树添加边:2-3 0.17
------------------------------------
添加节点3-6 0.52
删除最小节点:2-3 0.17
删除最小节点:0-2 0.26
删除最小节点:5-7 0.28
树添加边:5-7 0.28
------------------------------------
添加节点4-5 0.35
删除最小节点:5-7 0.28
删除最小节点:1-3 0.29
删除最小节点:1-3 0.29
删除最小节点:1-5 0.32
删除最小节点:1-5 0.32
删除最小节点:2-7 0.34
删除最小节点:2-7 0.34
删除最小节点:4-5 0.35
树添加边:4-5 0.35
------------------------------------
添加节点6-4 0.93
删除最小节点:4-5 0.35
删除最小节点:1-2 0.36
删除最小节点:1-2 0.36
删除最小节点:4-7 0.37
删除最小节点:4-7 0.37
删除最小节点:0-4 0.38
删除最小节点:0-4 0.38
删除最小节点:6-2 0.40
树添加边:6-2 0.40
------------------------------------
删除最小节点:6-2 0.40
删除最小节点:3-6 0.52
删除最小节点:3-6 0.52
删除最小节点:6-0 0.58
删除最小节点:6-0 0.58
删除最小节点:6-4 0.93
删除最小节点:6-4 0.93
最小生成树:
0-7 0.16
1-7 0.19
0-2 0.26
2-3 0.17
5-7 0.28
4-5 0.35
6-2 0.40