问题描述
假设要在六个城市之间架设电视网线,5条边可以连接6个点,在这里我们应该选择哪5条边呢?不同的城市之间搭建线路的造价是有差异的。 所以,我们应该选择总造价成本最低的5条线路,符合这个要求的线路选择就是带权最小生成树。
prin算法实现思路
- 先随意选择一个顶点作为起始点,一般选择A作为起始点,现在我们设U集合为当前所找到最小生成树里面的顶点,TE集合为所找到的边,现在状态如下:U={A};TE={};
- 现在查找一个顶点在U集合中,另一个顶点在V-U集合中的最小权值,如下图,在红线相交的线上找最小值。通过图中我们可以看到边A-D的权值最小为4,那么将D加入到U集合,(A,D)加入到TE,状态如下:U={A,D};TE={(A,D)};
- 继续寻找,现在状态为U={A,D};TE={(A,D)};在与红线相交的边上查找最小值。通过图中我们可以看到边A-B的权值最小为6,那么将B加入到U集合,(A,B)加入到TE,状态如下:U={A,D,B};TE={(A,D),(A,B)};
- 如此循环,直到找到所有顶点为止,也就是集合U里的顶点和原集合V一样的时候。
Java实现
封装顶点的类
package wgraph;
/**
* 顶点的类
*/
public class Vertex {
public char label;
public boolean isInTree;
public Vertex(char label){
this.label = label;
isInTree = false;
}
}
封装边的类
package wgraph;
/**
* 封装边的类
*/
public class Edge {
public int startVert;
public int endVert;
public int price;//权重
public Edge(int startVert, int endVert, int price){
this.startVert = startVert;
this.endVert = endVert;
this.price = price;
}
}
盛放边集合的优先级队列
package wgraph;
/**
* 盛放边的优先级队列
*/
public class ProprotyQ {
private Edge[] edges;//不是个普通数组,是有序的数组,从大到小排列
private final int MAX_SIZE = 20;//上面数组的默认长度
private int size;//上面数组中真实保存的边的条数
public ProprotyQ(){
edges = new Edge[MAX_SIZE];
size = 0;
}
//向队列中插入新的边
public void insert(Edge edge){
int i;
for (i=0;i<size;i++){
if (edge.price>=edges[i].price){
break;
}
}
//循环结束后,i停留在应该插入的位置
//数组中i之后的元素后移
for (int j=size-1;j>=i;j--){
edges[j+1] = edges[j];
}
//将新的边插入到i的位置
edges[i] = edge;
size++;
}
//删除并获取到队列中最小权的边
public Edge removeMin(){
return edges[--size];
}
//删除指定下标的边
public void removeN(int n){
for (int i=n;i<size;i++){
edges[i] = edges[i+1];
}
size--;
}
//获取最小的边,不删除
public Edge peekMin() {
return edges[size-1];
}
//判断队列是不是空
public boolean isEmpty(){
return size ==0;
}
public int getSize(){
return size;
}
//获取指定下标的边
public Edge peekN(int n){
return edges[n];
}
}
实现带权图最小生成树的类
package wgraph;
/**
* 实现带权图最小生成树的类
*/
public class WGraph {
//图的基本属性
private Vertex[] vertexList;//保存顶点的数组
private int[][] adjMat;//邻接矩阵
private int nVerts;//图中存在的节点数量
private final int MAX_VERTS = 20;//初始化一个图中的顶点最大的个数
private ProprotyQ proprotyQ;//存放最小生成树中的边的优先级数组
private int nTree;//已经求解到的最小生成树的顶点的下标
private int currentVert;//当前顶点的下标,下面循环会用
private final int INF = 1000000;//一个很大的数,在带权重的图中表示不连通
//构造方法
public WGraph(){
vertexList = new Vertex[MAX_VERTS];
adjMat = new int[MAX_VERTS][MAX_VERTS];
for (int i=0;i<MAX_VERTS;i++){
for (int j=0;j<MAX_VERTS;j++){
adjMat[i][j] = INF;
}
}
nVerts = 0;//初始状态,图内没有节点
proprotyQ = new ProprotyQ();
}
//向图中插入新的顶点
public void insert(char label){
vertexList[nVerts++] = new Vertex(label);
}
//更新边,设置顶点的连接关系
public void addEdge(int start, int end, int price){
//要更新邻接矩阵中的两个元素
adjMat[start][end] = price;
adjMat[end][start] = price;
}
//打印指定的顶点中的label
public void displayVertex(int v){
System.out.print(vertexList[v].label);
}
//实现求解带权值的最小生成树
public void mstw(){
//第一个步骤,任意选择一个顶点
currentVert = 0;
while (nTree<nVerts-1){
vertexList[currentVert].isInTree = true;//相当于把节点放入U中
nTree++;
for (int i=0;i<nVerts;i++){
if (i==currentVert) continue;//遍历到了自己,不做操作
if (vertexList[i].isInTree) continue;//遍历到的点已经在U集合里,不做操作
int price = adjMat[currentVert][i];//对应边的权值
if (price == INF) continue;//不邻接的顶点,不作操作
//剩下的情况就是找到了符合要求的邻接顶点
proprotyQ.insert(new Edge(currentVert,i,price));//把边插入优先级数组
}
if (proprotyQ.getSize()==0){
System.out.println("当前顶点没有邻接顶点");
return;//结束方法
}
//把以放到U集合中的顶点为起点和终点的边从优先级队列中删除
for (int i=0;i<proprotyQ.getSize();i++){
if (vertexList[proprotyQ.peekN(i).startVert].isInTree &&
vertexList[proprotyQ.peekN(i).endVert].isInTree){
//起点和重点都在U集合中
proprotyQ.removeN(i);
}
}
Edge minEdge = proprotyQ.removeMin();//拿到权值最小的点
int startV = minEdge.startVert;
currentVert = minEdge.endVert;
//输出找到的边,相当于把目标边放到TE集合中
System.out.print(vertexList[startV].label);
System.out.print(vertexList[currentVert].label);
System.out.print(" ");
}
//把所有顶点的isInTree还原
for (int i=0;i<nVerts;i++){
vertexList[i].isInTree = false;
}
}
}
测试带权图最小生成树的类
package wgraph;
public class GraphMain {
public static void main(String[] args) {
WGraph theGraph = new WGraph();
theGraph.insert('A'); //0
theGraph.insert('B');//1
theGraph.insert('C');//2
theGraph.insert('D');//3
theGraph.insert('E');//4
theGraph.insert('F');//5
theGraph.addEdge(0, 1, 6); // AB 6
theGraph.addEdge(0, 3, 4); // AD 4
theGraph.addEdge(1, 2, 10); // BC 10
theGraph.addEdge(1, 3, 7); // BD 7
theGraph.addEdge(1, 4, 7); // BE 7
theGraph.addEdge(2, 3, 8); // CD 8
theGraph.addEdge(2, 4, 5); // CE 5
theGraph.addEdge(2, 5, 6); // CF 6
theGraph.addEdge(3, 4, 12); // DE 12
theGraph.addEdge(4, 5, 7); // EF 7
theGraph.mstw();
}
}