Water2Wine

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

图的最短路径&最小生成树的以点为中心思想的算法总结

在图的常见问题中,求最短路径和图的最小生成树问题最为常见。而关于这两种问题,有一种算法思想可以用几乎相同的代码解决两种不同的问题。

这种算法思想的基础基于点,分别对应了最短路径问题中的Dijkstra算法和最小生成树问题中的prim算法。

这种算法思想的大致过程为:

1. 初始化

这里面涉及一些属性的初始化,首先是点集和边集,然后是一个存储了每个点到起点/生成树的距离的数组,还有一个判断点所属点集的boolean类型数组。

点集分为两个点集,已经在最短距离/最小生产树中的点U,其他点V。

private static final int INF = Integer.MAX_VALUE;
    // 首先,一个无项图需要有边集和点集
    private char[] vertexs;
    private int[][] edges;
    public Matrix(char[] c, int[][] e){
        this.vertexs = c;
        this.edges = e;
    }
    private static int[] dist;
    private static int[] prev;
    private static boolean[] isShortest;
    // 接下来是确定最短路径的方法distTo
    public void distTo(int start, int end){
        // 首先进行初始化
        dist = new int[vertexs.length];
        for(int i = 0;i < vertexs.length;i++){
            dist[i] = edges[start][i];
        }
        prev = new int[vertexs.length];
        Arrays.fill(prev, start);
        isShortest = new boolean[vertexs.length];
        // 然后将start的相关信息改一下
        isShortest[start] = true;
2. 贪心求点集

进行n-1次循环,每次循环将一个点加入到最小生成树/最短路径的点集中去

这其中包含两个步骤:

第一个步骤是在V中找到距离U/起点最近的点,并将该点加入U中

第二步是将V中剩余的点进行一轮松弛

// 接下来进行n-1次循环,每一次循环将一个点的isShortest转换为true,也就是每一次循环加入一条最短路径
        int k = start;
        for(int i = 1;i < vertexs.length;i++){
            // 找到未松弛点集中距离松弛点集最近的那一个点
            int min = INF;
            for(int j = 0;j < vertexs.length;j++){
                if(!isShortest[j] && dist[j] < min){
                    min = dist[j];
                    k = j;
                }
            }
            isShortest[k] = true;
            if(k == end){
                print(start, end);
                return;
            }
            // 将所有未松弛点集进行一波松弛
            for(int j = 0;j < vertexs.length;j++){
                if(!isShortest[j] && dist[j] > edges[j][k] + dist[k]){
                    dist[j] = edges[j][k] + dist[k];
                    prev[j] = k;
                }
            }
3. 输出

根据前两个步骤得到的U,即可输出最短路径的指定序列/最小生成树的路径长度

private static void print(int start, int end){
        if(start == end){
            System.out.print(start);
            return;
        }
        System.out.print(end + "->");
        print(start, prev[end]);
    }
posted on 2020-05-04 23:38  Water2Wine  阅读(208)  评论(0编辑  收藏  举报