迪杰斯特拉(Dijkstra)算法

最短路径问题:从某个顶点出发到达另外一个顶点的所经过的边的权重和最小的一条路径

 

迪杰斯特拉算法

1、应用场景

(1)用于查找图中某个顶点到其它所有顶点的最短路径,该算法既适用于无向加权图,也适用于有向加权图

(2)注意,使用迪杰斯特拉算法查找最短路径时,必须保证图中所有边的权值为非负数,否则查找过程很容易出错

2、实现思路

(1)无向加权图

(2)假设用迪杰斯特拉算法查找从顶点 0 到其它顶点的最短路径

(3)统计从顶点 0 直达其它顶点的权值,如下表所示,∞ 表示两个顶点之间无法直达,对应的权值为无穷大

(4)表 1:顶点 0 直达其它顶点的权值

  1 2 3 4 5 6
总权值 2 6
路径 0-1 0-2 0-3 0-4 0-5 0-6

(5)表 1 中,权值最小的是 0-1 路径,它也是从顶点 0 到顶点 1 的最短路径,从顶点 0 出发一共只有 0-1 和 0-2 两条路径,0-2 的权值本就比 0-1 大,所以从 0-2 出发不可能找得到比 0-1 权值更小的路径

(6)找到最短路径 0-1 后,沿 0-1 路径方向查找更短的到达其它顶点的路径,并对表 1 进行更新

(7)表 2 沿 0-1 最短路径更新表 1

  1 2 3 4 5 6
总权值 2 6 2+5
路径 0-1 0-2 0-1-3 0-4 0-5 0-6

(8)沿 0-1 路径可以到达顶点 3,且 0-1-3 的总权值比 0-3 更小。表 2 中,总权值最小的路径是 0-2,它也是从顶点 0 到顶点 2 的最短路径,如下图所示

(9)重复之前的操作,沿 0-2 路径方向查找更短的到达其它顶点的路径。但是,从顶点 2 只能到达顶点 3,且 0-2-3 的总权值比表 2 中记录的 0-1-3 更大,因此表 2 中记录的数据维持不变

(10)表 3 结合 0-2 最短路径更新表 2

  1 2 3 4 5 6
总权值 2 6 7
路径 0-1 0-2 0-1-3 0-4 0-5 0-6

(11)表 3 中,总权值最小的是 0-1-3,它也是顶点 0 到顶点 3 的最短路径

(12)沿 0-1-3 路径方向,查找到其它顶点更短的路径并更新表 3

(13)表 4 结合 0-1-3 最短路径更新表 3

  1 2 3 4 5 6
总权值 2 6 7 7+10 7+15
路径 0-1 0-2 0-1-3 0-1-3-4 0-1-3-5 0-6

(14)表 4 中,总权值最小的是 0-1-3-4,它是顶点 0 到顶点 4 的最短路径

(15)从顶点 4 出发,查找顶点 0 到其它顶点更短的路径并更新表 4

(16)表 5 结合 0-1-3-4 最短路径更新表 4

  1 2 3 4 5 6
总权值 2 6 7 17 22 17+2
路径 0-1 0-2 0-1-3 0-1-3-4 0-1-3-5 0-1-3-4-6

(17)表 5 中,总权值最小的路径是 0-1-3-4-6,它是顶点 0 到顶点 6 的最短路径

(18)只剩下顶点 0 到顶点 5 的最短路径尚未确定。从顶点 6 出发到达顶点 5 的路径是 0-1-3-4-6-5,对应的总权值为 25,大于表 5 中记录的 0-1-3-5 路径,因此 0-1-3-5 是顶点 0 到顶点 5 的最短路径

(19)找出了顶点 0 到其它所有顶点的最短路径

  1 2 3 4 5 6
总权值 2 6 7 17 22 19
路径 0-1 0-2 0-1-3 0-1-3-4 0-1-3-5 0-1-3-4-6

 

迪杰斯特拉(Dijkstra)算法解决最短路径问题

1、特点:是以起始点为中心向外层层扩展(广度优先),直到扩展到终点为止

2、基本思想:通过选定的被访问顶点,求出从出发访问顶点到其他顶点的最短路径

3、步骤

(1)设置出发顶点为 v,顶点集合 V,v 到 V 中各顶点的距离构成距离集合 D,记录着 v 到图中各顶点的距离(到自身可以看作 0)

(2)从 D 中选择最小距离值并移出集合 D,同时移出 V 集合中对应的顶点 u,此时的 v 到 u 即为最短路径

(3)更新集合 D,更新规则:比较 v 到 V 集合中顶点的距离值,与 v 通过 u 到 V 集合中顶点的距离值,保留值较小的一个,同时也应该更新顶点的前驱节点为 u,表明是通过 u 到达的

(4)重复执行步骤(2)(3),直到最短路径顶点为目标顶点即可结束

4、Dijkstra 算法能够解决边权重非负的加权有向图的单起点最短路径问题

5、在一幅含有 V 个顶点和 E 条边的加权有向图中,使用 Dijkstra 算法计算根结点为给定起点的最短路径树

(1)所需空间与 V 成正比

(2)所需时间与 ElogV 成正比(最坏情况下)

 

代码实现

import java.util.Arrays;

public class Dijkstra {//迪杰斯特拉算法解决最短路径问题
    public static final int BLOCK = 65535;//表示顶点之间不直接连通,顶点自身不连通

    public static void main(String[] args) {
        char[] vertex = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
        int[][] matrix = {
                {BLOCK, 5, 7, BLOCK, BLOCK, BLOCK, 2},
                {5, BLOCK, BLOCK, 9, BLOCK, BLOCK, 3},
                {7, BLOCK, BLOCK, BLOCK, 8, BLOCK, BLOCK},
                {BLOCK, 9, BLOCK, BLOCK, BLOCK, 4, BLOCK},
                {BLOCK, BLOCK, 8, BLOCK, BLOCK, 5, 4},
                {BLOCK, BLOCK, BLOCK, 4, 5, BLOCK, 6},
                {2, 3, BLOCK, BLOCK, 4, 6, BLOCK}
        };
        Graph graph = new Graph(vertex, matrix);
        graph.dijkstra(6);
        graph.show();
    }
}

//带权无向图
class Graph {
    public char[] vertex;//顶点数组
    public int[][] matrix;//邻接矩阵,一维数组的元素代表边的权值
    public VisitedVertex visitedVertex;//已访问顶点的集合
    public int startIndex;//记录起始点下标

    //构造器
    public Graph(char[] vertex, int[][] matrix) {
        this.vertex = vertex;
        this.matrix = matrix;
    }

    //迪杰斯特拉算法
    public void dijkstra(int index) {
        startIndex = index;
        visitedVertex = new VisitedVertex(vertex.length, startIndex);
        update(startIndex);//更新出发顶点到周围顶点的距离和前驱结点
        for (int i = 1; i < vertex.length; i++) {
            index = visitedVertex.updateVisited();
            update(index);
        }
    }

    //以index顶点为中间节点,更新出发点到i顶点的距离,且index顶点作为i顶点的前驱结点
    public void update(int index) {
        int length;
        //以index顶点遍历i顶点
        for (int i = 0; i < matrix[index].length; i++) {
            //出发顶点到index顶点的距离 + 从index顶点到i顶点的距离和
            length = visitedVertex.distance[index] + matrix[index][i];
            //如果i顶点没有被访问过,并且length小于出发顶点到i顶点的距离,就需要更新
            if (!visitedVertex.visited[i] && length < visitedVertex.distance[i]) {
                visitedVertex.pre[i] = index;//更新i的前驱结点为index
                visitedVertex.distance[i] = length;//更新出发顶点到i顶点的距离
            }
        }
    }

    //打印算法结果
    public void show() {
        for (int i = 0; i < vertex.length; i++) {
            if (i == startIndex) {
                System.out.println(vertex[i] + "为起始节点,没有前驱节点");
            } else {
                System.out.println(vertex[i] + " 前驱节点为 " + vertex[visitedVertex.pre[i]]);
            }
        }
        System.out.println();
        for (int i = 0; i < vertex.length; i++) {
            System.out.println(vertex[i] + " 到 " + vertex[startIndex] + " 的最短距离 " + visitedVertex.distance[i]);
        }
    }
}

//已访问顶点集合
class VisitedVertex {
    //动态更新
    public boolean[] visited;//各个顶点是否访问过;true:已访问,false:未访问
    public int[] pre;//数组下标对应vertex顶点下标,值为对应vertex顶点的前驱结点下标
    public int[] distance;//记录出发顶点到其他所有顶点的最短距离

    //length:表示顶点的个数;index:出发顶点对应的下标
    public VisitedVertex(int length, int index) {
        this.visited = new boolean[length];
        this.pre = new int[length];
        this.distance = new int[length];
        Arrays.fill(pre, -1);//初始化pre数组,默认-1,代表没有前驱节点
        Arrays.fill(distance, Dijkstra.BLOCK);//初始化distance数组,默认顶点之间都不连通
        distance[index] = 0;//设置出发顶点距离为0
        this.visited[index] = true;//设置出发顶点为已访问
    }

    //选择未访问,且与出发点的距离最短的顶点,并返回
    public int updateVisited() {
        int minDistance = Dijkstra.BLOCK, index = 0;
        for (int i = 0; i < visited.length; i++) {
            //新的访问节点,必须为未访问,且与出发点的距离最短
            if (!visited[i] && distance[i] < minDistance) {
                minDistance = distance[i];
                index = i;
            }
        }
        visited[index] = true;//更新index顶点被访问过
        return index;
    }
}
posted @   半条咸鱼  阅读(371)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示