最小生成树 prim算法


在无向加权图中,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());
}

}


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

2011052215540276.jpg


结果:

输出最小生成树的边:

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

可以发现这5条边是跟图中生成过程的顺序一样,依次找到放入数组的。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值