【算法】求最短路径算法

这篇具有很好参考价值的文章主要介绍了【算法】求最短路径算法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、迪杰斯特拉算法

1.1 算法介绍

从某顶点出发,沿图的边到达另一顶点所经过的路径中,各边上权值之和最小的一条路径叫做最短路径。 解决最短路径的问题有以下算法:Dijkstra
算法,Bellman-Ford 算法,Floyd 算法和 SPFA 算法等。

  • 迪杰斯特拉算法(Dijkstra 算法)是典型最短路径算法,用于计算一个节点到其它节点的最短路径,它的主要特点是以起始点为中心向外层层扩展(广度优先搜索思想),直到扩展到终点为止。

  • 该算法的时间复杂度为 O(ElogV),其中 E 为边数,V 为节点数。

1.2 算法步骤

  1. 设置起始点,并有记录各顶点的数组A、记录各顶点到起始点距离的数组B(起始点到自身的距离记作0);
  2. 从距离数组中选取最小的值(即当前为最短路径),选好以后,将对应的顶点从数组A中移除,对应记录的距离从数组B中移除;
  3. 比较起始点与各顶点的距离值,更新前驱节点,并保留值较小的一个距离值,从而完成对各顶点到起始点距离的数组B的更新;
  4. 重复执行步骤2、步骤3,直到最短路径顶点为目标顶点时,算法结束。

1.3 应用场景

假设有 7 个村庄(A,B,C,D,E,F,G),各个村庄的距离用边线表示(权) ,比如 A<–>B 距离 5 公里。问:如何计算出 G
村庄到其它各个村庄的最短距离?如果从其它点出发到各个点的最短距离又是多少?(通过迪杰斯特拉算法求最短路径)

示意图:
【算法】求最短路径算法

代码示例:文章来源地址https://www.toymoban.com/news/detail-431665.html

public class DijkstraAlgorithmDemo {

    public static void main(String[] args) {
        // 顶点集合。
        char[] vertexes = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};

        // 邻接矩阵,`Integer.MAX_VALUE` 表示不联通。
        final int x = Integer.MAX_VALUE;
        int[][] matrix = {
                {x, 5, 7, x, x, x, 2},
                {5, x, x, 9, x, x, 3},
                {7, x, x, x, 8, x, x},
                {x, 9, x, x, x, 4, x},
                {x, x, 8, x, x, 5, 4},
                {x, x, x, 4, 5, x, 6},
                {2, 3, x, x, 4, 6, x}
        };

        // 创建图。
        Graph graph = new Graph(vertexes, matrix);
        // 查看矩阵创建。
        graph.showGraph();
        // [x, 5, 7, x, x, x, 2]
        // [5, x, x, 9, x, x, 3]
        // [7, x, x, x, 8, x, x]
        // [x, 9, x, x, x, 4, x]
        // [x, x, 8, x, x, 5, 4]
        // [x, x, x, 4, 5, x, 6]
        // [2, 3, x, x, 4, 6, x]

        // 测试迪杰斯特拉算法
        // [6]:"G"([下标]:对应顶点)。
        int vertexIndex = 6;
        graph.dijkstra(vertexIndex);
        graph.dijkstraResult(vertexes, vertexIndex);
        // 各顶点访问状态 (0:表示已访问 1:表示未访问): 1 1 1 1 1 1 1
        // 前一顶点: 6 6 0 5 6 6 0
        // 各顶点到起始顶点的距离: 2 3 9 10 4 6 0
        // 具体详情如下:<G-A>:(2) <G-B>:(3) <G-C>:(9) <G-D>:(10) <G-E>:(4) <G-F>:(6) <G-G>:(0)
    }
}


/**
 * 图
 */
class Graph {

    /**
     * 顶点数组。
     */
    private final char[] vertexes;

    /**
     * 邻接矩阵。
     */
    private final int[][] matrix;

    /**
     * 已经访问的顶点的集合。
     */
    private VisitedVertex visitedVertex;

    /**
     * 初始化构造。
     *
     * @param vertexes 顶点
     * @param matrix   矩阵
     */
    public Graph(char[] vertexes, int[][] matrix) {
        this.vertexes = vertexes;
        this.matrix = matrix;
    }

    /**
     * 展示dijkstra算法结果。
     *
     * @param vertexes    顶点数组
     * @param vertexIndex 顶点下标
     */
    public void dijkstraResult(char[] vertexes, int vertexIndex) {
        visitedVertex.show(vertexes, vertexIndex);
    }

    /**
     * 展示图。
     */
    public void showGraph() {
        for (int[] link : matrix) {
            // 将表示不联通的值替换输出(便于查看)。
            System.out.println(Arrays.toString(link)
                    .replaceAll(Integer.MAX_VALUE + "", "x")
            );
        }
    }

    /**
     * 迪杰斯特拉算法。
     * 目的:根据起始顶点下标找到最短路径。
     *
     * @param index 顶点对应的下标(比如:0对应顶点A,1对应顶点B...)
     */
    public void dijkstra(int index) {
        int vertexNum = this.vertexes.length;
        this.visitedVertex = new VisitedVertex(vertexNum, index);
        // 更新当前顶点到其它周围顶点的距离和前驱顶点。
        update(index);
        for (int i = 1; i < vertexNum; i++) {
            // 选择并返回新的访问顶点。
            index = this.visitedVertex.updateArray();
            // 更新当前顶点到其它顶点的距离和前驱顶点。
            update(index);
        }
    }

    /**
     * 更新下标顶点到周围顶点的距离及前驱顶点。
     *
     * @param index 对应下标
     */
    private void update(int index) {
        // 遍历邻接矩阵。
        int len = this.matrix[index].length;
        long distance = 0;
        for (int j = 0; j < len; j++) {
            // `distance`表示:当前顶点到起始顶点的距离 + 从当前顶点到j顶点距离的和。
            distance = this.visitedVertex.getDistance(index) + this.matrix[index][j];
            // 如果j顶点没有被访问过,且求得的距离小于起始顶点到j顶点的距离,就需要进行更新。
            if (!visitedVertex.in(j) && distance < this.visitedVertex.getDistance(j)) {
                // 更新j顶点前驱顶点。
                this.visitedVertex.updatePre(j, index);
                // 更加起始顶点到j顶点的距离。
                this.visitedVertex.updateDistance(j, distance);
            }
        }
    }
}


/**
 * 已访问的顶点集。
 */
class VisitedVertex {

    /**
     * 记录顶点是否被访问过。
     * 0表示未访问,1表示访问过。
     */
    private final int[] isVisited;

    /**
     * 记录前驱顶点。
     */
    private final int[] preVisited;

    /**
     * 记录到起始顶点距离。
     */
    private final long[] distances;

    /**
     * 使用 `Integer.MAX_VALUE` 表示无穷大,即不可联通。
     */
    private static final int DISCONNECTED = Integer.MAX_VALUE;

    /**
     * 初始化构造。
     *
     * @param vertexNum 顶点个数
     * @param index     顶点对应的下标(比如:0对应顶点A,1对应顶点B...)
     */
    public VisitedVertex(int vertexNum, int index) {
        this.isVisited = new int[vertexNum];
        this.preVisited = new int[vertexNum];
        this.distances = new long[vertexNum];
        // 初始化距离数组数据。
        Arrays.fill(distances, DISCONNECTED);
        // 将起始顶点标记为已访问,并将访问距离设置为0。
        this.isVisited[index] = 1;
        this.distances[index] = 0;
    }

    /**
     * 顶点是否被访问过。
     *
     * @param index 顶点下标
     * @return boolean
     */
    public boolean in(int index) {
        return 1 == this.isVisited[index];
    }

    /**
     * 更新距离。
     *
     * @param index    顶点下标
     * @param distance 距离
     */
    public void updateDistance(int index, long distance) {
        this.distances[index] = distance;
    }

    /**
     * 更新前驱顶点
     *
     * @param pre   前驱顶点下标
     * @param index 当前顶点下标
     */
    public void updatePre(int pre, int index) {
        preVisited[pre] = index;
    }

    /**
     * 得到到起始顶点距离。
     *
     * @param index 顶点下标
     * @return long
     */
    public long getDistance(int index) {
        return this.distances[index];
    }

    /**
     * 继续选择并返回新的访问顶点,比如这里的G 完后,就是 A点作为新的访问顶点(注意不是起始顶点)。
     *
     * @return int
     */
    public int updateArray() {
        long minDis = DISCONNECTED;
        int index = 0;
        for (int i = 0; i < isVisited.length; i++) {
            // 若没有被访问过且有权值,则进行更新。
            if (0 == isVisited[i] && minDis > distances[i]) {
                minDis = distances[i];
                index = i;
            }
        }

        // 设置已访问。
        isVisited[index] = 1;
        return index;
    }

    /**
     * 将各数组数据情况进行输出并显示最终结果。
     *
     * @param vertexes    顶点数组
     * @param vertexIndex 顶点下标
     */
    public void show(char[] vertexes, int vertexIndex) {

        System.out.println("==========================");

        System.out.print("各顶点访问状态 (0:表示已访问 1:表示未访问): ");
        for (int i : isVisited) {
            System.out.print(i + " ");
        }
        System.out.println();

        System.out.print("前一顶点: ");
        for (int i : preVisited) {
            System.out.print(i + " ");
        }
        System.out.println();

        System.out.print("各顶点到起始顶点的距离: ");
        for (long i : distances) {
            System.out.print(i + " ");
        }
        System.out.println();

        System.out.print("具体详情如下:");
        int counter = 0;
        for (long i : distances) {
            if (DISCONNECTED != i) {
                System.out.print("<" + vertexes[vertexIndex] + "-" + vertexes[counter] + ">:(" + i + ") ");
            } else {
                System.out.println("N ");
            }
            counter++;
        }
        System.out.println();
    }
}

二、弗洛伊德算法

2.1 算法介绍

弗洛伊德算法(Floyd 算法)又称为插点法,是一种利用动态规划的思想寻找给定的加权图中多源点之间最短路径的算法,与 Dijkstra
算法类似。

  • 与迪杰斯特拉算法对比:

    • 迪杰斯特拉算法通过选定的被访问顶点,求出从起始访问顶点到其它顶点的最短路径;
    • 弗洛伊德算法中每一个顶点都是起始访问点,所以需要将每一个顶点看做被访问顶点,求出从每一个顶点到其它顶点的最短路径。
  • 该算法的时间复杂度为 O(n³),其中 n 为节点数。

  • 该算法可以处理有向图和无向图,但不能处理负权环,如果存在负权环,则最短路径不存在。

2.2 算法步骤

  1. 初始化距离矩阵:初始化一个 n*n 的矩阵D,其中 D[ i ][ j ] 表示从节点 i 到节点 j 的最短路径长度。如果 i 和 j 之间没有边相连,则 D[ i ][ j ] 为无穷大。
  2. 通过中间点更新距离矩阵:对于每个节点 k,依次更新矩阵D。具体地,对于每对节点 i 和 j,如果从 i 到 j 的路径经过节点 k 可以使得路径长度更短,则更新 D[ i ][ j ] 为 D[ i ][ k ]+D[ k ][ j ] 。
  3. 最终得到的矩阵D即为所有节点对之间的最短路径长度。

2.3 应用场景

假设有 7 个村庄(A,B,C,D,E,F,G),各个村庄的距离用边线表示(权),比如 A<–>B 距离 5
公里。问:如何计算出各村庄到其它各村庄的最短距离?(通过弗洛伊德算法求最短路径)

示意图:
【算法】求最短路径算法

代码示例:

public class FloydAlgorithmDemo {

    public static void main(String[] args) {
        // 顶点集合。
        char[] vertexes = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
        
        // 邻接矩阵,`Integer.MAX_VALUE` 表示不联通。
        final int x = Integer.MAX_VALUE;
        long[][] matrix = {
                {0, 5, 7, x, x, x, 2},
                {5, 0, x, 9, x, x, 3},
                {7, x, 0, x, 8, x, x},
                {x, 9, x, 0, x, 4, x},
                {x, x, 8, x, 0, 5, 4},
                {x, x, x, 4, 5, 0, 6},
                {2, 3, x, x, 4, 6, 0}
        };
        
        // 创建图。
        Graph graph = new Graph(vertexes.length, vertexes, matrix);
        // 测试弗洛伊德算法。
        graph.floyd();
        graph.show();
        // <A-A>的最短路径是0; <A-B>的最短路径是5; <A-C>的最短路径是7; <A-D>的最短路径是12; <A-E>的最短路径是6; <A-F>的最短路径是8; <A-G>的最短路径是2; 
        // <B-A>的最短路径是5; <B-B>的最短路径是0; <B-C>的最短路径是12; <B-D>的最短路径是9; <B-E>的最短路径是7; <B-F>的最短路径是9; <B-G>的最短路径是3; 
        // <C-A>的最短路径是7; <C-B>的最短路径是12; <C-C>的最短路径是0; <C-D>的最短路径是17; <C-E>的最短路径是8; <C-F>的最短路径是13; <C-G>的最短路径是9; 
        // <D-A>的最短路径是12; <D-B>的最短路径是9; <D-C>的最短路径是17; <D-D>的最短路径是0; <D-E>的最短路径是9; <D-F>的最短路径是4; <D-G>的最短路径是10; 
        // <E-A>的最短路径是6; <E-B>的最短路径是7; <E-C>的最短路径是8; <E-D>的最短路径是9; <E-E>的最短路径是0; <E-F>的最短路径是5; <E-G>的最短路径是4; 
        // <F-A>的最短路径是8; <F-B>的最短路径是9; <F-C>的最短路径是13; <F-D>的最短路径是4; <F-E>的最短路径是5; <F-F>的最短路径是0; <F-G>的最短路径是6; 
        // <G-A>的最短路径是2; <G-B>的最短路径是3; <G-C>的最短路径是9; <G-D>的最短路径是10; <G-E>的最短路径是4; <G-F>的最短路径是6; <G-G>的最短路径是0; 
    }
}

/**
 * 图。
 */
class Graph {

    /**
     * 顶点数组。
     */
    private final char[] vertexes;

    /**
     * 记录各个顶点到其它顶点的距离。
     */
    private final long[][] distances;

    /**
     * 到达目标顶点的前驱顶点。
     */
    private final int[][] preVisited;

    /**
     * 初始化构造。
     *
     * @param size      数组大小
     * @param vertexes  顶点集合
     * @param distances 距离
     */
    public Graph(int size, char[] vertexes, long[][] distances) {
        this.vertexes = vertexes;
        this.distances = distances;
        this.preVisited = new int[size][size];
        for (int i = 0; i < size; i++) {
            // 对前驱节点数组进行初始化(存放前驱顶点下标)。
            Arrays.fill(preVisited[i], i);
        }
    }

    /**
     * 展示前驱关系及距离信息。
     */
    public void show() {
        int length = this.distances.length;
        for (int k = 0; k < length; k++) {
            for (int i = 0; i < length; i++) {
                System.out.print("<" + this.vertexes[k] + "-" + this.vertexes[i] + ">的最短路径是" + distances[k][i] + "; ");
            }
            System.out.println();
        }
    }

    /**
     * 弗洛伊德算法。
     */
    public void floyd() {
        // 距离变量。
        long dist = 0;
        int dLen = this.distances.length;
        // 三层 `for`。
        // k 就是中间顶点的下标。
        for (int k = 0; k < dLen; k++) {
            // i 作为起始顶点。
            for (int i = 0; i < dLen; i++) {
                // j 作为目标顶点。
                for (int j = 0; j < dLen; j++) {
                    // 求出从 i 顶点出发,经过 k 中间顶点,到达 j 顶点距离。
                    dist = distances[i][k] + distances[k][j];
                    // 如果`算法得到的距离`小于`当前距离表中已有的距离`则进行更新。
                    if (dist < distances[i][j]) {
                        // 更新距离。
                        distances[i][j] = dist;
                        // 更新前驱顶点。
                        preVisited[i][j] = preVisited[k][j];
                    }
                }
            }
        }
    }
}

到了这里,关于【算法】求最短路径算法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【任务分配】多目标粒子群算法求解多无人机多任务路分配及路径规划(最短路程+最短时间)问题【含Matlab源码 3522期】

    1 粒子群算法 粒子群算法是智能算法领域中除蚁群算法、鱼群算法又一个智能群体算法。 PSO算法首先在可行解空间中初始化一群粒子,每个粒子都代表极值优化问题的一个潜在最优解。粒子在解空间中运动,通过跟踪个体极值Pbest和群体极值Gbest更新个体位置。 粒子每更新一次

    2024年02月04日
    浏览(50)
  • 【算法】求最短路径算法

    从某顶点出发,沿图的边到达另一顶点所经过的路径中,各边上权值之和最小的一条路径叫做最短路径。 解决最短路径的问题有以下算法:Dijkstra 算法,Bellman-Ford 算法,Floyd 算法和 SPFA 算法等。 迪杰斯特拉算法(Dijkstra 算法)是典型最短路径算法,用于计算一个节点到其它

    2024年02月02日
    浏览(21)
  • 图算法——求最短路径(Dijkstra算法)

            目录 一、什么是最短路径 二、迪杰斯特拉(Dijkstra)算法  三、应用Dijkstra算法 (1) Dijkstra算法函数分析         求图的最短路径在实际生活中有许多应用,比如说在你在一个景区的某个景点,参观完后,要怎么走最少的路程到你想参观的下个景点,这就利用到

    2023年04月15日
    浏览(28)
  • 图算法——求最短路径(Floyd算法)

    目录 一、什么是最短路径 二、弗洛伊德(Floyd)算法 三、测试程序         求图的最短路径在实际生活中有许多应用,比如说在你在一个景区的某个景点,参观完后,要怎么走最少的路程到你想参观的下个景点,这就利用到了求图最短路径的算法。求图的最短路径有很多

    2024年02月07日
    浏览(26)
  • Dijkstra算法求最短路

    Dijkstra算法的流程如下: 1.初始化dist[1] = 0,其余节点的dist值为无穷大。 2.找出一个未被标记的、dist[x]最小的节点x,然后标记节点x。 3.扫描节点x的所有出边(x,y,z),若dist[y] dist[x] + z,则使用dist[x] + z更新dist[y]。 4.重复上述2~3两个步骤,直到所有的节点都被标记。 Dijk

    2024年02月06日
    浏览(33)
  • 弗洛伊德算法(求最短路径)

    在一个加权图中,如果想找到各个顶点之间的最短路径,可以考虑使用弗洛伊德算法。 弗洛伊德算法既适用于无向加权图,也适用于有向加权图。使用弗洛伊德算法查找最短路径时,只允许环路的权值为负数,其它路径的权值必须为非负数,否则算法执行过程会出错。 弗洛

    2024年02月06日
    浏览(30)
  • 求最短路径的三种算法

    目录 一.单源最短路 1.dijkstra算法及实现 2.spfa算法及实现 (1)spafa负环判断及实现 二.多源最短路 1.floyd算法及实现 一.单源最短路 1.dijkstra算法及实现 求源点到图中其余各顶点的最短路径 dfs效率慢,解决规模小,bfs只能边权为1的图 Dijkstra算法——迪杰斯塔拉算法(非负全图)

    2024年02月14日
    浏览(27)
  • 最短路径:迪杰斯特拉算法

    简介         英文名Dijkstra         作用:找到路中指定起点到指定终点的带权最短路径 核心步骤         1)确定起点,终点         2)从未走过的点中选取从起点到权值最小点作为中心点         3)如果满足 起点到中心点权值 + 中心点到指定其他点的

    2024年02月08日
    浏览(30)
  • 最短路径——迪杰斯特拉算法

    日常生活中常常涉及最短路径问题,如在一个城市交通网中,如何选取从起点到达终点的路径,才能使这一趟旅程的路程最短?或所需时间最少?或所需交通费用最低?诸如此类问题都可以抽象为 求解图的最短路径问题 。我们把 图的顶点 表示为 城市的交通站点 , 边表示交

    2024年02月04日
    浏览(37)
  • Acwing.854 Floyd求最短路 (Floyd算法)

    给定一个n个点m条边的有向图,图中可能存在重边和自环,边权可能为负数。 再给定k个询问,每个询问包含两个整数x和y,表示查询从点x到点y的最短距离,如果路径不存在,则输\\\"impossible”。 数据保证图中不存在负权回路。 第一行包含三个整数n, m, k 接下来m行,每行包含三

    2024年02月13日
    浏览(25)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包