数据结构与算法 24 普里姆算法 修路问题 克鲁斯卡尔算法 kruskal 公交站问题

普里姆算法

修路问题,给定村庄和距离,求将所有村庄联通,并且总的修建公路总里程最短?

最小生成树

最小生成树 Minimun Cost Spanning Tree

  1. 给定一个带权的无向连通图,如何选取一颗生成树,使树上所有边上权的总和为最小,就叫最小生成树
  2. N个顶点,一定有 N-1 条边
  3. 包含全部顶点
  4. N-1 条边都在图中
  5. 求最小生成树的算法主要是普里姆算法和克鲁斯卡尔算法

普里姆算法思路

  1. 普里姆(prim)算法求最小生成树,也就是在包含 n 个顶点的连通图中,找出只有(n-1)条边包含所有 n 个顶点的连通子图,也就是所谓的极小连通子图
  2. 算法如下:
    • 设 G=(V,E) 是连通图,T=(U,D)是最小生成树,V,U是顶点集合, E,D是边的集合
    • 若从顶点 u开始构造最小生成树,则从集合 V 中取出顶点u 放入集合U中,标记顶点v的 visited[u] = 1
    • 若集合 U 中顶点 ui与集合 V-U 中的顶点 vj之间存在边,则寻找这些边中权值最小的边,但不能构成回路,将顶点 vj 加入到集合 U 中,将边 (ui,vj) 加入集合 D 中,标记 visited[vj]=1
    • 重复步骤2,直到 U、V相等,即所有顶点都被标记为访问过,此时 D 有 n-1条边

算法代码步骤

  1. 创建图类,定义顶点数量、顶点、权值变量,写构造方法,初始化

    // create graph
    class MGraph{
        int vertexes; // number of vertex in graph
        char[] data; // store data node
        int[][] weight; // store edges; preliminary matrix
    
        public MGraph(int vertexes){
            this.vertexes = vertexes;
            data = new char[vertexes];
            weight = new int[vertexes][vertexes];
        }
    }
    
  2. 创建最小生成树类,编写创建图(给图分配顶点和权值)的方法和展示图的方法

    // build minimum cost spanning tree
    class MinTree{
    
        /**
         * create preliminary matrix of graph
         * @param graph graph object
         * @param vertexes number of vertex in graph
         * @param data value of each vertex
         * @param weight preliminary matrix of graph
         */
        public void createGraph(MGraph graph, int vertexes, char data[],int[][] weight){
            int i;
            int j;
            for (i = 0; i < vertexes; i++) {
                graph.data[i] = data[i];
                for(j = 0; j<vertexes;j++){
                    graph.weight[i][j] = weight[i][j];
                }
            }
        }
        // show graph
        public void showGraph(MGraph graph){
            for(int[] link:graph.weight){
                System.out.println(Arrays.toString(link));
            }
        }
    }
    
  3. 初始化变量,分配值,测试图

    public class PrimDemo {
        public static void main(String[] args) {
            char[] data = new char[]{'A','B','C','D','E','F','G'};
            int vertexes = data.length;
            // 10000 means these two points are not connected
            int[][] weight = new int[][]{
                    {10000,5,7,10000,10000,10000,2},
                    {5,10000,10000,9,10000,10000,3},
                    {7,10000,10000,10000,8,10000,10000},
                    {10000,9,10000,10000,10000,4,10000},
                    {10000,10000,8,10000,10000,5,4},
                    {10000,10000,10000,4,5,10000,6},
                    {2,3,10000,10000,4,6,10000}};
            // create MGraph
            MGraph graph = new MGraph(vertexes);
            // create minTree
            MinTree minTree = new MinTree();
            minTree.createGraph(graph,vertexes,data,weight);
            minTree.showGraph(graph);
        }
    }
    
  4. prim算法,在 MinTree class 中,传入图和起始点的坐标,创建visited数组

        /**
         * prim algor to get minimum cost spanning tree
         * @param graph
         * @param v index of vertex which start from; 'A'-0-v
         */
        public void prim(MGraph graph,int v){
            // whether vertex is visited
            int visited[] = new int[graph.vertexes];
            // default value of visited[] is 0 (in java)
    //        for(int i = 0;i<graph.vertexes;i++){
    //            visited[i] = 0;
    //        }
    
            // mark this node as visited
            visited[v] = 1;
            // h1,h2 record index of two vertexes
            int h1 = -1;
            int h2 = -1;
            int minWeight = 10000; // initialize a big number
            for(int k = 1;k<graph.vertexes;k++){ // there are graph.vertexes-1 edges after prim
                // determine subgraph for each time with closest distance
                for(int i = 0;i<graph.vertexes;i++){
                    for(int j=0;j<graph.vertexes;j++){
                        // i is visited; j is not visited
                        if(visited[i] == 1 && visited[j] == 0 && graph.weight[i][j]<minWeight){
                            // replace minWeight
                            // find the edge with smallest weight between visited vertexes and non-visited vertex
                            minWeight = graph.weight[i][j];
                            h1 = i;
                            h2 = j;
                        }
                    }
                }
                // find the smallest edge
                System.out.println("edge: "+graph.data[h1]+","+graph.data[h2]+"-- weight"+minWeight);
                // mark h1,h2 as vistied
                visited[h2] = 1;
                // reset minWeight
                minWeight = 10000;
    
            }
        }
    

克鲁斯卡尔算法

应用:公交站问题

算法介绍

  1. Kruskal算法,用来求加权连通图的最小生成树的算法
  2. 按照权值从小到大的顺序选择 n-1 条边,并保证这 n-1 条边不构成回路
  3. 首先构造一个只含有n个顶点的森林,然后依照权值从小到大从连通网中选择边加入到森林中,并使森林中不产生回路,直到森林变成一棵树为止

**排序(权值从小到大) **:排序算法

将边添加到最小生成树中时(检查)回路:记录顶点在“最小生成树”中的终点,顶点的终点是“在最小生成树中与它连通的最大顶点”。然后每次需要将一条边添加到最小生成树时,判断该边的两个顶点的终点是否重合,重合的话则会构成回路(即加入的边的两个顶点不能指向同一个终点)


代码

package algorithm.kruskal;

import java.util.Arrays;

public class kruskalDemo {
    private int edgeNum; // num of edges
    private char[] vertexes; // vertex
    private int[][] matrix; // preliminary matrix
    // INF means two vertexes cannot connect
    private static final int INF = Integer.MAX_VALUE;
    public static void main(String[] args) {
        char[] vertexes = {'A','B','C','D','E','F','G'};
        int[][] matrix = {
                {0,12,INF,INF,INF,16,14},
                {12,0,10,INF,INF,7,INF},
                {INF,10,0,3,5,6,INF},
                {INF,INF,3,0,4,INF,INF},
                {INF,INF,5,4,0,2,8},
                {16,7,6,INF,2,0,9},
                {14,INF,INF,INF,8,9,0}};
        // create kruskalDemo object
        kruskalDemo kruskalDemo = new kruskalDemo(vertexes, matrix);
        kruskalDemo.print();
        kruskalDemo.kruskal();
    }
    // constructor
    public kruskalDemo(char[] vertexes, int[][] matrix){
        // initialize vertexes and num of edges
        int vlen = vertexes.length;
        // initialize vertexes, using copy way
        this.vertexes = new char[vlen];
        for(int i = 0;i<vertexes.length;i++){
            this.vertexes[i] = vertexes[i];
        }
        // initialize edges
        this.matrix = new int[vlen][vlen];
        for(int i = 0;i<vlen;i++){
            for(int j = 0;j<vlen;j++){
                this.matrix[i][j] = matrix[i][j];
            }
        }
        // count edges
        for(int i=0;i< vlen;i++){
            for (int j = i+1; j < vlen; j++) {
                if(this.matrix[i][j]!=INF){
                    edgeNum++;
                }
            }
        }
    }

    public void kruskal(){
        int index = 0; // index of final result array
        int[] ends = new int[edgeNum]; // store end of vertex in minimum cost spanning tree in "tree already generated"
        // create array for result, storing tree
        EData[] rets = new EData[edgeNum];
        // get all edges in graph
        EData[] edges = getEdges();
        // sort by edge weight, ascending
        sortEdges(edges);
        // traversal edges array, add edge into minTree
        // determine whether the edge waiting for addition forms a circle
        // if not, add into rets, or cannot add
        for(int i = 0;i<edgeNum;i++){
            // get the first vertex of i-th edge
            int p1 = getPosition(edges[i].start);
            // get the second vertex of i-th edge
            int p2 = getPosition(edges[i].end);
            // get p1's end in minTree
            int m = getEnd(ends,p1);
            // get p2's end in minTree
            int n = getEnd(ends,p2);
            // whether it forms a circle
            if(m!=n){ // not forms circle
                ends[m] = n;
                rets[index++] = edges[i]; // add an edge into rets
            }
        }
        for (int i = 0; i < index; i++) {
            System.out.println(rets[i]);
        }
    }

    // print preliminary matrix
    public void print(){
        System.out.println("preliminary matrix: \n");
        for (int i = 0; i < vertexes.length; i++) {
            for (int j = 0; j < vertexes.length; j++) {
                System.out.printf("%13d\t",matrix[i][j]);
            }
            System.out.println();
        }
    }
    // sort: bubble sort
    private void sortEdges(EData[] edges){
        for(int i = 0; i<edges.length-1;i++){
            for (int j = 0; j < edges.length-1-i; j++) {
                if(edges[j].weight>edges[j+1].weight){
                    EData tmp = edges[j];
                    edges[j] = edges[j+1];
                    edges[j+1] = tmp;
                }
            }
        }
    }

    /**
     *
     * @param ch value of vertex, 'A' 'B'
     * @return return index of ch, return -1 if not found
     */
    private int getPosition(char ch){
        for(int i = 0;i< vertexes.length;i++){
            if(vertexes[i] == ch){
                return i;
            }
        }
        return -1;
    }

    /**
     * get edges of graph and put into EData[], for traversal
     * use maxtrix preliminary matrix
     * EData['A','B',12],['B','F',7]...
     * @return
     */
    private EData[] getEdges(){
        int index = 0;
        EData[] edges = new EData[edgeNum];
        for(int i = 0;i<vertexes.length;i++){
            for(int j = i+1;j<vertexes.length;j++){
                if(matrix[i][j] != INF){
                    edges[index++] = new EData(vertexes[i],vertexes[j],matrix[i][j]);
                }
            }
        }
        return edges;
    }

    /**
     * get end of vertex whose index is i, for determine is that same for two vertexes' end
     * @param ends record end of each vertex, form in the process of traversal
     * @param  i index of coming vertex
     * @return index of vertex end whose index is i
     */
    private int getEnd(int[] ends,int i){
        while(ends[i] != 0){
            i = ends[i];
        }
        return i;
    }
}

// create a class EData, its object instance represents an edge
class EData{
    char start; // start of edge
    char end; // end of edge
    int weight; // weight of edge
    // constructor
    public EData(char start,char end,int weight){
        this.start = start;
        this.end = end;
        this.weight = weight;
    }
    // overwrite toString

    @Override
    public String toString() {
        return "EData{" +
                "<" + start +
                "," + end +
                ">," + weight +
                '}';
    }
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值