最小生成树的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的实现:

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条边是跟图中生成过程的顺序一样,依次找到放入数组的。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值