克鲁斯卡尔之公交车站问题详解

克鲁斯卡尔之公交车站问题详解

说明

  1. 克鲁斯卡尔算法也是解决图的最短路径问题,即给定一个带权的无向图,求其最小生成树使得各个顶点之间的距离最短
  2. 公交车站问题是指有七个公交车站,修一条通路连接这七个公交车站,使得路劲最短
  3. 可以使用带权的无向图来模拟公交车站问题,因此可以使用克鲁斯卡尔算法解决
  4. 克鲁斯拉尔算法思路???
  5. 先求出各个顶点之间的路径,即权值,将这些路径按权值按照从小到大排序
  6. 然后依次获取每条路径的两个顶点,从权值最小的开始,将对应的顶点连接,但是在连接前,要先判断当前两个顶点的连接是否会构成通路,如果构成通路,则再判断下一条路径
  7. 判断是否构成通路的方法: 可以根据已连接最小生成树的终点判断,即判断要连接的两个顶点的终点是否是同一个顶点,如果是则构成通路,如果不是则没有
  8. 生成的最小生成树的路径应该为 顶点数 - 1条
  9. 克鲁斯卡尔算法的核心就是先连接距离最短的两个顶点,并不停的判断是否构成回路,先将所有最短的不构成回路的连接后,再考虑较长的路径,以达到较优的结果
  10. 源码见下

源码及分析

package algorithm.algorithm.kruskal;

/**
 * @author AIMX_INFO
 * @version 1.0
 */
@SuppressWarnings("ALL")
public class Kruskal {
    //边的个数
    private int edgeNum;
    //顶点的值
    private char[] vertex;
    //邻接矩阵
    private int[][] matrix;
    //定义一个最大数表示未连通
    private static final int INF = Integer.MAX_VALUE;

    public static void main(String[] args) {
        //顶点
        char[] vertex = {'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}
        };
        Kruskal kruskal = new Kruskal(vertex, matrix);
        kruskal.kruskal();


    }

    //构造器

    /**
     * @param vertex 顶点个数
     * @param matrix 邻接矩阵
     */
    public Kruskal(char[] vertex, int[][] matrix) {
        //初始化顶点数和边的个数
        //根据实参确定顶点个数
        int vLen = vertex.length;
        //使用值传递的方式初始化顶点值
        this.vertex = new char[vLen];
        for (int i = 0; i < vLen; i++) {
            this.vertex[i] = vertex[i];
        }
        //使用值传递的方法初始化邻接矩阵
        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];
            }
        }
        //统计边
        for (int i = 0; i < vLen; i++) {
            for (int j = i + 1; j < vLen; j++) {
                if (this.matrix[i][j] != INF) {
                    edgeNum++;
                }
            }
        }
    }

    //打印邻接矩阵
    public void print() {
        System.out.println("邻接矩阵:");
        for (int i = 0; i < vertex.length; i++) {
            for (int j = 0; j < vertex.length; j++) {
                System.out.printf("%12d", matrix[i][j]);
            }
            System.out.println();
        }
    }
    //使用冒泡排序根据边的权值对边进行排序

    /**
     * @param edges 边的集合
     */
    public void sortEdge(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;
                }
            }
        }
    }

    //根据顶点的值返回顶点在数组中的位置,即下标
    public int getPosition(char v) {
        for (int i = 0; i < vertex.length; i++) {
            if (v == vertex[i]) {
                return i;
            }
        }
        return -1;
    }

    //获取途中的边,放到EData[]数组中
    public EData[] getEdges() {
        int index = 0;
        EData[] edges = new EData[edgeNum];
        for (int i = 0; i < vertex.length; i++) {
            for (int j = i + 1; j < vertex.length; j++) {
                if (matrix[i][j] != INF) {
                    edges[index++] = new EData(vertex[i], vertex[j], matrix[i][j]);
                }
            }
        }
        return edges;
    }

    //获取下标为 i 的顶点的终点
    public int getEnd(int[] ends, int i) {
        while (ends[i] != 0) {
            i = ends[i];
        }
        return i;
    }

    //克鲁斯卡尔算法
    public void kruskal() {
        //index保存最后结果数组的索引
        int index = 0;
        //用于保存已有最小生成树的每个顶点在最小生成树中的终点
        int[] ends = new int[edgeNum];
        //创建结果数组,保存最终的结果
        EData[] res = new EData[edgeNum];

        //获取图中所有边的集合
        EData[] edges = getEdges();
        //对边按权值排序
        sortEdge(edges);
        //遍历edges数组,判断当前边是否和已有最小生成树构成回路,如果没有则加入
        for (int i = 0; i < edges.length; i++) {
            //获取第i条边的第一个顶点
            int p1 = getPosition(edges[i].start);
            //获取第i条边的第二个顶点
            int p2 = getPosition(edges[i].end);

            //获取两个顶点在最小生成树中的终点
            int m = getEnd(ends, p1);
            int n = getEnd(ends, p2);
            //如果终点不同,说明没有构成回路
            if (m != n) {
                //先将起始点的终点指为终点
                ends[m] = n;
                //再将该边加入结果集
                res[index++] = edges[i];
            }
        }
        System.out.println("最小生成树:");
        for (int i = 0; i < index; i++) {
            if (res[i] != null) {
                System.out.println(res[i]);
            }
        }
    }
}

//边类
class EData {
    //边的一个顶点
    char start;
    //边的另一个顶点
    char end;
    //边的权值
    int weight;

    //构造器
    public EData(char start, char end, int weight) {
        this.start = start;
        this.end = end;
        this.weight = weight;
    }

    @Override
    public String toString() {
        return "[" +
                start + "-" +
                end +
                "] --> " + weight
                ;
    }
}

posted @ 2021-06-17 16:45  mx_info  阅读(161)  评论(0)    收藏  举报