图的连通性---最小生成树(Prim算法)

1.现在要选择这样一个生成树,也就是使总耗费最少。这个问题就是构造连通网的最小代价生成树(Minimum Cost Spanning Tree)简称为最小生成树的问题。一颗生成树的代价就是树上各边的代价之和。

构造最小生成树的算法有多种。大多算法利用了最小生成树的下列简称为MST的特性:

假设N=(V,{E})是一个连通网,(N代表连通网,V代表网的各个顶点,E代表网的边),U是顶点集V的一个非空子集。若(u,v)是一条具有最小权值的边,其中u属于U,v属于V-U,则必存在一颗包含边(u,v)的最小生成树。

2.反证法证明上面MST特性:

假设网N的任意一颗最小生成树都不包含(u,v).设T是连通网上的一颗最小生成树,当将边(u,v)加入T中时,由生成树的定义可知,T中必存在一条包含(u,v)的回路。

另一方面,由于T是生成树,则在T中必存在一条(u1,v1),其中u1属于U,v1属于V-U,且u,u1,v,v1必有路径相通。删去(u1,v1)便可消除上述回路,同时得到另一颗生成树T1。由于(u,v)的代价不高于(u1,v1),则T1的代价不高于T,T1是包含(u,v)的一颗最小生成树。由此可假设矛盾。

3.普里姆(prim)算法 

假设N=(V,{E})是连通网,TE是N上最小的生成树中边的集合。算法从U={u0},(u0属于V),TE={}开始,重复执行下述操作:在所有u属于U,v属于V-U的边(u,v)属于E中,寻找一条代价最小的边(u0,v0)并入集合TE中,同时v0也并入集合U中,直到U=V为止。此时TE中必有n-1条边,而T=(V,{E})为N的最小生成树。

为实现这个算法,需要附设一个辅助数组closedge,以记录从U到U-V具有最小代价的边。对每个顶点vi属于V-U,在辅助数组中存在一个相应分量closedge[i-1],它包含两个域,其中lowcost存储该边上的权。vex存储附属在该边上的在U中的顶点。 

/**
     * 普里姆算法,最小生成树,
     * 从图中第start个元素开始,生成最小树
     */
    public void prim(int start) {
        Integer vexNum = mVexs.length; //顶点的个数
        int index = 0;//prim最小树的索引,即prims数组的索引
        String[] prims = new String[vexNum]; //prim最小树的结果数组
        int[] weights = new int[vexNum]; //顶点间的权值
        prims[index++] = mVexs[start].data; //prim生成树的第一个顶点是从start开始的

        /**
         * 初始化顶点的权值数组;
         * 将每个顶点的权值初始化为第start个顶点到该顶点的权值
         */
        for (int i = 0; i < vexNum; i++) {
            weights[i] = getWeight(start, i);
        }


        for (int i = 0; i < vexNum; i++) {
            if (start == i) {
                continue; //终止本次循环,进行下一次循环,break终止for中的循环,进行循环下面的语句
            }
            int j = 0, k = 0, min = INF;
            //在未被加入最小生成树的顶点中,找出权值最小的顶点
            while (j < vexNum) {
                //当weights[j]=0时,意味着第j个顶点已经被排序过,或者说已经加入了最小生成树中
                if (weights[j] != 0 & weights[j] < min) {
                    min = weights[j];
                    k = j;  //将最小权值的顶点赋给k
                }
                j++;
            }

            /**
             *  经过上面的while循环,则知在未被加入到最小生成树的顶点中,权值最小的是k
             *  将第k个顶点加入到prims数组中
             */
            prims[index++] = mVexs[k].data;
            //并将第k个顶点的权值标记为0,意味着第k个顶点已经被排序过了或者说被加入到了最小生成树的结果中
            weights[k] = 0;

            //更新其他顶点到最小生成树中顶点的权值
            for (j = 0; j < vexNum; j++) {
                //获取第k个顶点到第j个顶点的权值
                int tmp = getWeight(k, j);
                //当第j个顶点未在最小生成树中,且第j个顶点到最小生成树中顶点的权值需要更新时,即加入k顶点后,已不再是最小权值时
                if (weights[j] != 0 && tmp < weights[j]) {
                    weights[j] = tmp;
                }
            }
        }
        /**
         * 当prim最小生成树已经生成完毕时
         * 计算最小生成树的权值
         */
        int sum = 0;
        //要从第2个元素开始,索引为1,因为要计算两顶点之间的权值
        for (int i = 1; i < index; i++) {
            int min = INF;
            //或者去prims[i]顶点在顶点中的位置
            int n = getPostion(prims[i]);
            for (int j = 0; j < i; j++) {
                int m = getPostion(prims[j]);
                int tmp = getWeight(m, n);
                if (tmp < min) {
                    min = tmp;

                }
            }
            sum += min;

        }


        /**
         * 打印最小生成树
         */
        System.out.println("PRIM" + mVexs[start].data + "=" + sum);
        for (int i = 0; i < index; i++) {
            System.out.println(prims[i]);

        }
    }

 

转载于:https://my.oschina.net/u/2263272/blog/1610199

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值