最小生成树 prim算法

来源:互联网 发布:化工仿真软件csts 编辑:程序博客网 时间:2024/06/03 18:46
在无向加权图中,n个顶点的最小生成树有n-1条边,这些边使得n个顶点之间可达,且总的代价最小。

prim算法是一种贪心算法,将全部的顶点划分为2个集合,每次总在2个集合之间中找最小的一条边,局部最优最终达到全局最优,这正是贪心的思想。
具体的描述参见相关书籍:

描述

从单一顶点开始,普里姆算法按照以下步骤逐步扩大树中所含顶点的数目,直到遍及连通图的所有顶点。

  1. 输入:一个加权连通图,其中顶点集合为V,边集合为E;
  2. 初始化:Vnew = {x},其中x为集合V中的任一节点(起始点),Enew = {};
  3. 重复下列操作,直到Vnew = V:
    1. 在集合E中选取权值最小的边(u, v),其中u为集合Vnew中的元素,而v则不是(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一);
    2. 将v加入集合Vnew中,将(u, v)加入集合Enew中;
  4. 输出:使用集合Vnew和Enew来描述所得到的最小生成树。

    参考:维基百科 prim算法    http://zh.wikipedia.org/wiki/Prim%E6%BC%94%E7%AE%97%E6%B3%95


    prim的实现:

    复制代码
    1 //prim最小生成树
    2
    3 public Edge[] getEdges(int position){ //返回从顶点position开始的最小生成树的边数组
    4 Edge[] edges = new Edge[size()-1]; //最小生成树里边数为n-1,!!!!size()
    5 VNodes[position].setVisited(true); //将原来的遍历中用的标志在这分离集合
    6 for(int i = 0;i < edges.length;i++) //找n-1条边出来
    7 {
    8 Edge edge = getMinEdge(VNodes); //在当前分离的2个集合之间找到最小边
    9 edges[i] = edge;
    10 VNodes[edge.getEnd()].setVisited(true);//将新添加的边的另一端的顶点分离出来
    11 }
    12 return edges;
    13 }
    14
    15 private Edge getMinEdge(VNode[] VNodes){ //从分离的2个集合之间求出最小的边
    16 return min;
    17 }
    18
    19 private boolean hasEdge(VNode node){ //判断某个标记true的顶点跟另一个集合之间是否有边
    20 return false;
    21 }
    22
    23
    24 private Edge getMinEdgeFrom(VNode node){ //如果有边(前提),求出这个顶点到对方集合的最小边
    25 return min;
    26 }
    27
    28
    复制代码


    因为我存的是顶点,所以还要找边,比较麻烦一点,如果在图中记录了一个边得数组,就可以直接在边得数组里面去找最小边。

    解决问题的思路是先很容易就可以写出上述最小生成树的逻辑实现,然后去一一实现支持它的方法。
三个支持方法的实现:

复制代码
private Edge getMinEdge(VNode[] VNodes){ //从分离的2个集合之间求出最小的边
Edge min = null;
//for(int i = 0;i < VNodes.length;i++)
for(int i = 0;i < size();i++)
{
if(VNodes[i].getVisited() && hasEdge(VNodes[i]))//从true集合向false集合找
{
Edge temp
= getMinEdgeFrom(VNodes[i]);
if(min == null)//第一次的初始化min
min = temp;
else if(temp.getLen() < min.getLen())
min
= temp;
}
}
return min;
}

private boolean hasEdge(VNode node){ //判断某个标记true的顶点跟另一个集合之间是否有边
if(node.getFirst() == null)
return false;
else
{
Edge temp
= node.getFirst();
while(temp != null)
{
int index = temp.getEnd();
if(VNodes[index].getVisited() == false)
return true;
else temp = temp.getNext();
}
}
return false;
}


private Edge getMinEdgeFrom(VNode node){ //如果有边(前提),求出这个顶点到对方集合的最小边
Edge min = null;
Edge temp
= node.getFirst();

while(temp != null)
{
int index = temp.getEnd();
if(VNodes[index].getVisited() == false)//说明此时的temp是到对方集合的一条边
{
if(min == null)
min
= temp;
else if(temp.getLen() < min.getLen())
min
= temp;
}
temp
= temp.getNext();
}
return min;
}
复制代码



然后添加一个图来测试一下:

复制代码
public static void main(String[] args) {
// TODO Auto-generated method stub
UnDirectedGraph g = new UnDirectedGraph();

for(int i = 1;i < 7;i++)
g.addVNode(
"V" + i);
g.addEdge(
0, 1, 6);
g.addEdge(
0, 2, 1);
g.addEdge(
0, 3, 5);
g.addEdge(
1, 2, 5);
g.addEdge(
1, 4, 5);
g.addEdge(
2, 3, 5);
g.addEdge(
2, 4, 6);
g.addEdge(
2, 5, 4);
g.addEdge(
3, 5, 2);
g.addEdge(
4, 5, 6);//严蔚敏数据结构中的那个图

Edge[] edges
= g.getEdges(0);
System.out.println(
"输出最小生成树的边");
for(int i = 0;i < edges.length;i++)
{
int start = edges[i].getStart();
int end = edges[i].getEnd();
System.out.println(
"边: " + g.VNodes[start].getVNode() +"---" + g.VNodes[end].getVNode()
+ " 长度:" + edges[i].getLen());
}

}
复制代码


构造了如下的图(清华《数据结构》   最小生成树的那个图):




结果:

输出最小生成树的边: 

边: V1---V3   长度:1
边: V3---V6   长度:4
边: V6---V4   长度:2
边: V3---V2   长度:5
边: V2---V5   长度:5

可以发现这5条边是跟图中生成过程的顺序一样,依次找到放入数组的
0 0
原创粉丝点击