数据结构与算法之图(四)图的最小生成树

引言

现在假设有一个很实际的问题:我们要在n个城市中建立一个通信网络,则连通这n个城市需要布置n-1一条通信线路,这个时候我们需要考虑如何在成本最低的情况下建立这个通信网?
于是我们就可以引入连通图来解决我们遇到的问题,n个城市就是图上的n个顶点,然后,边表示两个城市的通信线路,每条边上的权重就是我们搭建这条线路所需要的成本,所以现在我们有n个顶点的连通网可以建立不同的生成树,每一颗生成树都可以作为一个通信网,当我们构造这个连通网所花的成本最小时,搭建该连通网的生成树,就称为最小生成树。

最小生成树算法思路

构造最小生成树有很多算法,但是他们都是利用了最小生成树的同一种性质:MST性质:假设图N=(V,E)是一个连通网,U是顶点集V的一个非空子集,如果(u,v)是一条具有最小权值的边,其中u属于U,v属于V-U,则必定存在一颗包含边(u,v)的最小生成树,今天我们学习使用MST性质生成最小生成树的算法:普里姆算法。
普里姆算法的关键就是寻找集合U 和V-U之间的最小权边(u,v),然后把v顶点加入U,它把顶点集分割成两部分,每一次操作就从V-U寻找顶点v,把它放到集合U中,这样保证生成树没有回环,俗称"避圈法"。实现流程如下:
1.原始图:



2.假设我们从顶点v1开始,所以我们可以发现(v1,v3)边的权重最小,所以第一个输出的边就是:(v1,v3):



3.从v1和v3作为起点的边中寻找权重最小的边,发现(v3,v6)这条边最小,所以输出边就是(v3,v6):

4.从v1、v3、v6这三个点相关联的边中寻找一条权重最小的边,我们可以发现边(v6,v4)权重最小,所以输出边就是(v6,v4):

5.从v1、v3、v6、v4这四个顶点相关联的边中寻找权重最小的边,发现边(v3, v2)的权重最小,所以输出边:(v3, v2):



6.从v1、v3、v6、v4、v2这五个顶点相关联的边中寻找权重最小的边,发现边(v2,v5)的权重最小,所以输出边(v2,v5):

7.六个顶点全部连接,最小生成树构建完成.
算法流程:
1>设u为起点,无向图的邻接矩阵C[N][N],U为已经加入生成树的顶点集合,V-U为未加入生成树的顶点集合,s[]数组做为顶点所属集合的标记。closest[]数组存放V-U中顶点j到集合U中距离最近的顶点索引,如上面的步骤2中,closest[3]=1,表示顶点V3的最近顶点为V1.lowcost[]数组存放V-U集合中到U最近顶点的边权值,如步骤2中,lowcost[3]=1,表示V3到集合{V1}的最小权重为1.
2>.初始化U={u},closest[]、lowcost[]和s[].
3>.在V-U中寻找lowcost[]最小的顶点t,加入集合U.
4>.更新lowcost和closest,更新公式:如果C[t][j] 5>步骤2、3、4循环执行N次。

代码实现

package graphic;

/**
 * Created by chenming on 2018/6/18
 * 最小生成树算法-普利姆算法
 */
public class Prim {
    private int[][] map;
    //V-U集合中距离U集合中顶点最近的点,如closest[j] = i,
    // 表示j距离U集合最近的顶点为i,在最小树中,i为j的前驱
    private int[] closest;
    private int[] lowcost;//V-U集合中距离U最近顶点的边长(j, closest[j])
    private boolean[] s;//标记是否添加到集合U

    public Prim(int[][] map) {
        this.map = map;
        int size = map.length;
        closest = new int[size];
        lowcost = new int[size];
        s = new boolean[size];
    }


    /**
     * 构造最小生成树
     *
     * @param u0 起点顶点
     */
    public void prim(int u0) {
        int n = map.length;
        //Step1初始化
        s[u0] = true;//u0加入集合
        for (int i = 0; i < n; i++) {
            if (i != u0) {
                lowcost[i] = map[u0][i];
                closest[i] = u0;
            } else {
                lowcost[i] = 0;
                closest[i] = -1;//-1表示没有前驱
            }
        }

        //Step2 S-U中寻找距离U最小的顶点t,加入集合,更新lowcost和closest
        for (int i = 0; i < n; i++) {
            //寻找
            int min = Integer.MAX_VALUE;
            //找最小权边顶点t
            int t = u0;
            for (int j = 0; j < n; j++) {
                if (!s[j] && lowcost[j] < min) {
                    t = j;
                    min = lowcost[j];
                }
            }

            //加入集合
            if (t == u0) {//没找到最小值,跳出循环
                break;
            }
            s[t] = true;
            //更新lowcost和closest,从最小顶点t出发,如果它的邻接顶点j的边权小于lowcost[j],则更新lowcost和closest
            for (int j = 0; j < n; j++) {
                if (!s[j] && map[t][j] < lowcost[j]) {
                    lowcost[j] = map[t][j];
                    closest[j] = t;
                }
            }

        }

        //输出结果
        dumpResult();
    }

    /**
     * 输出边和顶点即可
     */
    private void dumpResult() {
        System.out.println("======最小树结构======");
        for (int i = 0; i < lowcost.length; i++) {
            System.out.println("顶点" + i + "的前驱:" + closest[i]);
        }
        System.out.println("======最小树权值======");
        for (int i = 0; i < lowcost.length; i++) {
            System.out.println("权值:" + lowcost[i]);
        }
    }
}

测试代码:

 @Test
    public void testPrim() {
        int INF = Integer.MAX_VALUE;
        int[][] map = {
                {INF, 23, INF, INF, INF, 28, 36},
                {23, INF, 20, INF, INF, INF, 1},
                {INF, 20, INF, 15, INF, INF, 4},
                {INF, INF, 15, INF, 3, INF, 9},
                {INF, INF, INF, 3, INF, 17, 16},
                {28, INF, INF, INF, 17, INF, 25},
                {36, 1, 4, 9, 16, 25, INF}
        };

        Prim prim = new Prim(map);
        prim.prim(0);
    }

测试结果:

======最小树结构======
顶点0的前驱:-1
顶点1的前驱:0
顶点2的前驱:6
顶点3的前驱:6
顶点4的前驱:3
顶点5的前驱:4
顶点6的前驱:1
======最小树权值======
权值:0
权值:23
权值:4
权值:9
权值:3
权值:17
权值:1

完整代码地址:数据结构与算法学习JAVA描述GayHub地址

你可能感兴趣的:(数据结构与算法之图(四)图的最小生成树)