【算法】求最短路径算法

news/2024/12/4 22:22:52/

文章目录

  • 一、迪杰斯特拉算法
    • 1.1 算法介绍
    • 1.2 算法步骤
    • 1.3 应用场景
  • 二、弗洛伊德算法
    • 2.1 算法介绍
    • 2.2 算法步骤
    • 2.3 应用场景

一、迪杰斯特拉算法

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
村庄到其它各个村庄的最短距离?如果从其它点出发到各个点的最短距离又是多少?(通过迪杰斯特拉算法求最短路径)

示意图:
在这里插入图片描述

代码示例:

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];}}}}}
}

http://www.ppmy.cn/news/56141.html

相关文章

IGH EtherCAT主站应用层代码开发:控制驱动电机

1、安装IGH EtherCAT主站 Ubuntu18.04环境下安装igH EtherCAT Master 2、查询从站配置信息 连接从站通过网线连接主站与从站 启动主站打开终端,输入: sudo /etc/init.d/ethercat star 显示Starting EtherCAT master 1.5.2 done则说明成功。 查询从站列表终端输入: eth…

ARN (Yet Another Resource Negotiator)-HA (High Availability)

HA机制 YARN (Yet Another Resource Negotiator)是Hadoop的资源管理器&#xff0c;它负责管理集群中的资源分配和任务调度。在YARN中&#xff0c;HA (High Availability)机制是指在主节点出现故障时&#xff0c;能够自动地将任务管理权转移至备用节点上&#xff0c;从而保证系…

数据库预科与增删查改(CURD)

一、预科 1.分类 分为关系型数据库和非关系型数据库 关系型数据库对于数据库中数据的格式,要求比较严格(使用硬盘来存储数据) 非关系型数据库则相对不太严格,因此其功能相对于关系型数据库少一些,但是性能更高,因此更适应当前大数据分布式时代 关系型数据库的代表软件有Or…

常用图标(icon)css下载

1、演示图例&#xff08;icon1.css&#xff09;[24*18] 2、演示图例&#xff08;icon2.css&#xff09;[24*24] 3、演示图例&#xff08;icon3.css&#xff09;[24*24] 4、演示图例&#xff08;icon4.css&#xff09;[24*18] 5、演示图例&#xff08;icon5.css&#xff09;[26*…

【计算机网络】127.0.0.1、0.0.0.0、localhost地址是什么?

目录 0.0.0.0是什么&#xff1f;127.0.0.1是什么&#xff1f;用途 localhost是什么&#xff1f;总结 0.0.0.0是什么&#xff1f; IPV4中&#xff0c;0.0.0.0地址被用于表示一个无效的&#xff0c;未知的或者不可用的目标。 在服务器中&#xff0c;0.0.0.0指的是本机上的所有I…

“华为杯”第十七届中国研究生 数学建模竞赛-【华为杯】B题:降低汽油精制过程中的辛烷值损失模型(附优秀论文)

目录 摘 要: 一、问题重述 二、基本假设 三、本题研究流程 四、数据处理过程与分析

ARM架构基本理论(1)

ARM架构基本理论 一、ARM的简介 ARM&#xff08;Advanced RISC Machine&#xff09;是一种基于RISC&#xff08;Reduced Instruction Set Computing&#xff09;架构的计算机处理器架构&#xff0c;由ARM Holdings&#xff08;ARM公司&#xff09;开发和授权给其他公司生产和…

MySQL数据库——MySQL UPDATE:修改数据(更新数据)

在 MySQL 中&#xff0c;可以使用 UPDATE 语句来修改、更新一个或多个表的数据。 UPDATE 语句的基本语法 使用 UPDATE 语句修改单个表&#xff0c;语法格式为&#xff1a; UPDATE <表名> SET 字段 1值 1 [,字段 2值 2… ] [WHERE 子句 ] [ORDER BY 子句] [LIMIT 子句]…