最短路径相关算法

news2024/11/17 14:44:50

文章目录

  • 图论中的图属性
  • 最短路径算法
    • - Dijkstra算法
      • 1. 算法介绍
      • 2. 适用场景
      • 3. 场景举例
    • - Bellman-Ford算法
      • 1. 算法介绍
      • 2. 适用场景
      • 3. 场景举例
    • - Floyd-Warshall算法
      • 1. 算法介绍
      • 2. 适用场景
      • 3. 场景举例
  • 具体实现方案
    • - JGraphT
  • 小结


图论中的图属性

图论中,图的属性可以分为以下几个方面:

  • 顶点:每个顶点(节点)可以具有自定义的属性,例如标签、值、坐标等。

  • :每条边可以具有自定义的属性,例如权重(用于有权图)、方向(有向图)、容量(用于网络流图)等。

  • 连通性:描述节点之间是否存在路径或连通的关系。如果图中的任意两个节点之间都存在路径,称为全连通图。如果只有部分节点之间存在路径,称为非连通图。

  • 有权图:(Weighted Graph)是由一组顶点和一组边组成的图,每条边都有一个权重或者成本与之关联。这些权重可以表示两个顶点之间的距离、成本、容量等。在有权图中,每条边的权重可以为正数、零或负数。

  • 无权图:(Unweighted Graph),又称为简单图或等权图,是由一组顶点和一组边组成的图,每条边都没有权重或者权重都相同。在无权图中,所有的边都被视为具有相等的权重或距离,所以边的权重通常被设定为1。

  • 负权图:(Negative Weighted Graph)是有权图的一种特殊情况,其中至少存在一条边的权重为负数。在负权图中,路径的总权重可能为负数,这表示在此路径上从起点到终点的总消耗为负值。负权图在某些算法和问题中具有特殊的应用,例如最短路径算法中的"负权回路"问题。

  • 有向图:(Directed Graph)表示图中的边具有方向性,即从一个节点指向另一个节点的关系。

  • 无向图:(Undirected Graph)表示图中的边没有方向性,即节点之间的关系是双向的。

  • 完全图:(Complete Graph)在一个无向图中,如果每对节点之间都有边连接,则称为完全图。在一个有向图中,如果每对节点之间都有方向相反的边连接,则称为强连通完全图。

最短路径算法

最短路径算法是图论中的经典问题,用于寻找两个节点之间的最短路径。下面是几种常见的最短路径算法。

- Dijkstra算法

1. 算法介绍

Dijkstra算法用于解决单源最短路径问题,即从一个固定节点出发,到其他节点的最短路径。它基于贪心策略,逐步扩展最短路径集合,直到到达目标节点或无法继续扩展为止。

它的核心思想是通过逐步扩展到达源节点的最短路径树,在每一步选择当前最短路径上的节点进行扩展,直到所有节点都被包含在最短路径树中。

2. 适用场景

Dijkstra算法适用于无权图或非负权图。常用于路网规划、地图导航、网络路由等场景。

3. 场景举例

在一个地理地图中,当计算两个城市之间的最短路径或者规划一条最短行车路线时,Dijkstra算法可以被应用。

- Bellman-Ford算法

1. 算法介绍

Bellman-Ford算法用于解决单源最短路径问题,与Dijkstra算法不同,它可以处理含有负权边的图。算法通通过迭代计算每个节点的最短路径估计值,并逐渐收敛到最优解,检测负权环以避免无限循环。

它的核心思想是利用松弛操作(即更新节点的最短路径估计值),通过迭代进行多轮松弛操作,直到不再存在可以改进的最短路径。

2. 适用场景

Bellman-Ford算法适用于带有负权边的图。常用于网络拓扑计算、检测负权环等场景。

3. 场景举例

在一个计费系统中,当计算从一个节点到其他节点的最低费用时,Bellman-Ford算法可以被应用。

- Floyd-Warshall算法

1. 算法介绍

Floyd-Warshall算法用于解决全源最短路径问题,即求解任意两个节点之间的最短路径。该算法利用动态规划的思想,通过中转节点不断优化路径的长度,最终得到所有节点对之间的最短路径。

它的核心思想是通过中间节点的枚举来逐步逼近最短路径。算法创建一个二维矩阵来存储任意两个节点之间的最短路径长度,通过不断更新矩阵中的值来得到最终的最短路径。Floyd算法的时间复杂度为O(n^3),适用于节点数量不是很大的情况。

2. 适用场景

Floyd-Warshall算法适用于小规模图,可以求解所有节点对之间的最短路径。常用于交通网络路由、图数据库中的查询优化等场景。

3. 场景举例

在一个城市交通系统中,当计算任意两个交通站点之间的最短路径或者最佳路线时,Floyd-Warshall算法可以被应用。

具体实现方案

- JGraphT

JGraphT是一个用于图算法的Java库,它提供了许多常见的图算法实现,包括Dijkstra算法。Dijkstra算法用于找到带权图中从一个顶点到其他顶点的最短路径。

maven配置:

    <dependency>
        <groupId>org.jgrapht</groupId>
        <artifactId>jgrapht-core</artifactId>
        <version>1.4.0</version>
    </dependency>

路径图如下:
在这里插入图片描述

源码:

    /**
     * 最短路径
     * @param vertexs       顶点集合
     * @param edgeWeights   边权重集合
     * @param srcVertexs    源顶点集合
     * @param dstVertexs    目的顶点集合
     */
    public static List<ShortestPathInfo> shortestPath(List<String> vertexs, List<EdgeWeight> edgeWeights, List<String> srcVertexs, List<String> dstVertexs) {
        log.info("====================================== shortestPath start ======================================");
        StopWatch stopWatch = new StopWatch();

        // 创建有向加权图
        stopWatch.start("创建有向加权图");
        Graph<String, DefaultWeightedEdge> graph = new DefaultDirectedWeightedGraph<>(DefaultWeightedEdge.class);
        stopWatch.stop();
        log.info("{}耗时:{}毫秒", stopWatch.getLastTaskName(), stopWatch.getLastTaskTimeMillis());

        // 添加顶点
        stopWatch.start("添加顶点");
        for (String vertex : vertexs) {
            graph.addVertex(vertex);
        }
        stopWatch.stop();
        log.info("{}耗时:{}毫秒", stopWatch.getLastTaskName(), stopWatch.getLastTaskTimeMillis());

        // 添加边权重
        stopWatch.start("添加边权重");
        for (EdgeWeight edgeWeight : edgeWeights) {
            graph.addEdge(edgeWeight.getStartPoint(), edgeWeight.getEndPoint());
            graph.setEdgeWeight(edgeWeight.getStartPoint(), edgeWeight.getEndPoint(), edgeWeight.getWeight());
        }
        stopWatch.stop();
        log.info("{}耗时:{}毫秒", stopWatch.getLastTaskName(), stopWatch.getLastTaskTimeMillis());

        // 创建最短路径算法对象
        stopWatch.start("创建最短路径算法对象");
        DijkstraShortestPath<String, DefaultWeightedEdge> algorithm = new DijkstraShortestPath<>(graph);// - 创建DijkstraShortestPath对象
        // BellmanFordShortestPath<String, DefaultWeightedEdge> algorithm = new BellmanFordShortestPath<>(graph);// - 创建BellmanFordShortestPath对象
        // FloydWarshallShortestPaths<String, DefaultWeightedEdge> algorithm = new FloydWarshallShortestPaths<>(graph);// - 创建FloydWarshallShortestPaths对象
        stopWatch.stop();
        log.info("{}耗时:{}毫秒", stopWatch.getLastTaskName(), stopWatch.getLastTaskTimeMillis());

        // 原顶点集合和目标顶点集合
        stopWatch.start("原顶点集合和目标顶点集合");
        if (ObjectUtils.isEmpty(srcVertexs)) {
            srcVertexs = new ArrayList<>(graph.vertexSet());// 全部顶点
        }
        if (ObjectUtils.isEmpty(srcVertexs)) {
            dstVertexs = new ArrayList<>(graph.vertexSet());// 全部顶点
        }
        stopWatch.stop();
        log.info("{}耗时:{}毫秒", stopWatch.getLastTaskName(), stopWatch.getLastTaskTimeMillis());

        // 计算最短路径
        stopWatch.start("计算最短路径");
        List<ShortestPathInfo> list = new ArrayList<>();
        for (String src : srcVertexs) {
            for (String dst : dstVertexs) {
                if (!src.equals(dst)) {
                    GraphPath<String, DefaultWeightedEdge> path1 = algorithm.getPath(src, dst);
                    double pathWeight = algorithm.getPathWeight(src, dst);
                    ShortestPathInfo shortestPathInfo = new ShortestPathInfo(src, dst, path1 != null ? path1.getVertexList() : new ArrayList<>(), pathWeight);
                    list.add(shortestPathInfo);
                }
            }
        }
        stopWatch.stop();
        log.info("{}耗时:{}毫秒", stopWatch.getLastTaskName(), stopWatch.getLastTaskTimeMillis());
        log.info("总耗时:{}毫秒", stopWatch.getTotalTimeMillis());
        log.info("====================================== shortestPath end ======================================");
        return list;
    }


    public static void main(String[] args) {
        List<String> vertexs = Stream.of("A","B","C","D","E","F","G","H","I").collect(Collectors.toList());
        List<String> edgeWeightsList = new ArrayList<>();
        edgeWeightsList.add("A,B,4");
        edgeWeightsList.add("A,H,15");
        edgeWeightsList.add("H,A,12");
        edgeWeightsList.add("B,H,11");
        edgeWeightsList.add("H,I,7");
        edgeWeightsList.add("B,C,8");
        edgeWeightsList.add("H,G,1");
        edgeWeightsList.add("C,I,2");
        edgeWeightsList.add("I,G,6");
        edgeWeightsList.add("C,D,7");
        edgeWeightsList.add("C,F,4");
        edgeWeightsList.add("D,F,14");
        edgeWeightsList.add("D,E,9");
        edgeWeightsList.add("E,F,10");
        edgeWeightsList.add("G,F,2");
        List<EdgeWeight> edgeWeights = new ArrayList<>();
        for (String s : edgeWeightsList) {
            String[] split = s.split(",");
            edgeWeights.add(new EdgeWeight(split[0], split[1], Double.valueOf(split[2])));
        }
        List<String> srcVertexs = Stream.of("A","B","C").collect(Collectors.toList());
        List<String> dstVertexs = Stream.of("F","G","I").collect(Collectors.toList());
        List<ShortestPathInfo> list = ShortestPathUtil.shortestPath(vertexs, edgeWeights, srcVertexs, dstVertexs);
        list.stream().forEach(sp -> log.info("=====顶点{}-顶点{}=====最短路径:{}=====权重:{}", sp.getSrcVertex(), sp.getDstVertex(), sp.getPath().toString(), sp.getWeight()));
    }
}

输出:

00:08:51.208 [main] INFO com.joker.util.ShortestPathUtil - ====================================== shortestPath start ======================================
00:08:51.240 [main] INFO com.joker.util.ShortestPathUtil - 创建有向加权图耗时:26毫秒
00:08:51.245 [main] INFO com.joker.util.ShortestPathUtil - 添加顶点耗时:3毫秒
00:08:51.246 [main] INFO com.joker.util.ShortestPathUtil - 添加边权重耗时:1毫秒
00:08:51.256 [main] INFO com.joker.util.ShortestPathUtil - 创建最短路径算法对象耗时:9毫秒
00:08:51.260 [main] INFO com.joker.util.ShortestPathUtil - 原顶点集合和目标顶点集合耗时:4毫秒
00:08:51.278 [main] INFO com.joker.util.ShortestPathUtil - 计算最短路径耗时:17毫秒
00:08:51.278 [main] INFO com.joker.util.ShortestPathUtil - 总耗时:62毫秒
00:08:51.278 [main] INFO com.joker.util.ShortestPathUtil - ====================================== shortestPath end ======================================
00:08:51.279 [main] INFO com.joker.util.ShortestPathUtil - =====顶点A-顶点F=====最短路径:[A, B, C, F]=====权重:16.0
00:08:51.280 [main] INFO com.joker.util.ShortestPathUtil - =====顶点A-顶点G=====最短路径:[A, H, G]=====权重:16.0
00:08:51.280 [main] INFO com.joker.util.ShortestPathUtil - =====顶点A-顶点I=====最短路径:[A, B, C, I]=====权重:14.0
00:08:51.280 [main] INFO com.joker.util.ShortestPathUtil - =====顶点B-顶点F=====最短路径:[B, C, F]=====权重:12.0
00:08:51.280 [main] INFO com.joker.util.ShortestPathUtil - =====顶点B-顶点G=====最短路径:[B, H, G]=====权重:12.0
00:08:51.280 [main] INFO com.joker.util.ShortestPathUtil - =====顶点B-顶点I=====最短路径:[B, C, I]=====权重:10.0
00:08:51.280 [main] INFO com.joker.util.ShortestPathUtil - =====顶点C-顶点F=====最短路径:[C, F]=====权重:4.0
00:08:51.281 [main] INFO com.joker.util.ShortestPathUtil - =====顶点C-顶点G=====最短路径:[C, I, G]=====权重:8.0
00:08:51.281 [main] INFO com.joker.util.ShortestPathUtil - =====顶点C-顶点I=====最短路径:[C, I]=====权重:2.0

小结

这些最短路径算法各自有不同的适用场景和复杂度。选择合适的算法取决于图的特点(有向或无向、权重正负等)和问题的具体需求(单源或全源最短路径)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/711580.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringMVC (三) RestFul和控制器

学习回顾&#xff1a;SpringMVC &#xff08;一&#xff09; 什么是SpringMVC 现在我们来看看里面的控制器和路径请求的具体内容吧&#xff01; 一、控制器Controller 控制器复杂提供访问应用程序的行为&#xff0c;通常通过接口定义或注解定义两种方法实现。控制器负责解析用户…

如何在Centos7下安装Nginx

一、Nginx简介 Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器 &#xff0c;同时也提供了IMAP/POP3/SMTP服务。Nginx是由伊戈尔赛索耶夫为俄罗斯访问量第二的Rambler.ru站点&#xff08;俄文&#xff1a;Рамблер&#xff09;开发的&#xff0c;公开版本1.19.6…

Self-attention Transformer

参考资料&#xff1a; 《机器学习》李宏毅 1 Self-attention 当模型输入为长度不定的向量序列时&#xff08;如一段文字、一段语音、图模型&#xff09;&#xff0c;要求模型输出为等长的向量序列&#xff08;序列标注&#xff09;时&#xff0c;可以使用 Self-attention S…

文件的概念 + 文件的相对/绝对路径 + 文件IO字符流 / 字节流的读写操作 (java)

文章目录 前言一、文件是什么1.文件的概念2.文件路径1.绝对路径2.相对路径 二、针对文件的IO1.File类2.对文件内容的读写操作1.针对文本文件—— 字符流输入流 Reader类输出流 Writer类 2.针对二进制文件—— 字节流输入流 InputStream类输出流 OutputStream类 总结 前言 本人…

《Reinforcement Learning: An Introduction》第7章笔记

Chapter 7 n-step Bootstrapping 本章的n-step TD方法是前两章的Monte Carlo 方法和one-step TD方法的推广。它使自举法在多个时间步内进行&#xff0c;解决了前两章中的更新时间步不灵活的问题。 7.1 n-step TD Prediction 在用策略 π \pi π下生成的采样回合序列来估计 v…

UE5 MetaHuman SDK插件的使用【一、编辑器创建音波与蓝图创建获取音波,音波与嘴唇的同步】

目录 打开插件 创建音频 编辑器这直接创建音频&#xff1a; 蓝图中创建和获取音频&#xff1a; 唇语&#xff1a; 声音与嘴唇同步&#xff1a; 方法一【效果不是很好】&#xff1a; 方法二【效果很好&#xff0c;但有一段时间延迟在处理】&#xff1a; 逻辑&#xff1…

Spring Boot 中的微服务架构:原理和使用

Spring Boot 中的微服务架构&#xff1a;原理和使用 什么是微服务架构&#xff1f; 微服务架构是一种软件架构模式&#xff0c;它将一个应用程序分解成一组小的、松散耦合的服务。每个服务都有独立的进程和数据存储&#xff0c;可以独立地开发、部署、测试和扩展。这种架构模…

最新AI创作系统ChatGPT网站源码+新增GPT联网功能+支持GPT4+支持ai绘画+实时语音识别输入

最新AI创作系统ChatGPT网站源码新增GPT联网功能支持GPT4支持ai绘画实时语音识别输入 一、AI创作系统二、系统介绍三、系统程序下载四、安装教程五、其他主要功能展示六、更新日志 一、AI创作系统 提问&#xff1a;程序已经支持GPT3.5、GPT4.0接口、支持新建会话&#xff0c;上…

常见面试题之ThreadLocal的理解

1. ThreadLocal概述 ThreadLocal是多线程中对于解决线程安全的一个操作类&#xff0c;它会为每个线程都分配一个独立的线程副本从而解决了变量并发访问冲突的问题。ThreadLocal同时实现了线程内的资源共享 案例&#xff1a;使用JDBC操作数据库时&#xff0c;会将每一个线程的…

常微分方程的解题思路

通解&#xff1a;独立常数的个数等于微分方程的阶数&#xff0c;独立常数的个数实际上就是 c 1 , c 2 , . . . , c n c_1,c_2,...,c_n c1​,c2​,...,cn​是数目 所以补 C C C也是关键的一步&#xff0c;而且未必是 C C C&#xff0c;也可以是 ln ⁡ C \ln C lnC之类的&…

HPM6750系列--第六篇 创建自己的工程

一、目的 细心的朋友可能会发现官方提供了HPM-Sdk仓库&#xff0c;示例代码都是作为一个文件夹的形式放在此仓库下&#xff0c;我们之前都是直接拷贝里面的工程进行演示。 下面我们就具体来分析一下hello_world工程的文件组成&#xff0c;这样我们就可以创建自己的工程。 二、…

vscode里vue文件内<template>标签报错

发现只要把文件名使用驼峰命名法&#xff0c;把Login.vue改为LoginView.vue就不报错了加个大写的View就没有了。 官方参考文档&#xff1a;vue/multi-word-component-names | eslint-plugin-vue

说说@EnableConfigurationProperties那点事

两者的对比 ConfigurationProperties 使用ConfigurationProperties的时候&#xff0c;把配置类的属性与yml配置文件绑定起来的时候&#xff0c;还需要加上Component注解才能绑定并注入IOC容器中&#xff0c;若不加上Component&#xff0c;则会无效。 EnableConfigurationPro…

Mybatis 入门及实践

Mybatis 入门及实践 文章目录 Mybatis 入门及实践前言一. 简介二. 入手案例2.1 前置准备2.1.1 Maven依赖2.1.2 sql准备2.1.3 Log4j2 配置1. Maven引入2. log4j2.xml 2.2 代码流程构建步骤2.2.1 Mybatis前置知识2.2.2 步骤流程2.2.3 代码实现 三. XML映射器前置代码3.1 select3.…

【网络编程】网络编程套接字(一)

文章目录 网络编程预备知识认识端口号理解源端口号和目的端口号认识TCP协议和UDP协议网络字节序sockaddr结构1.socket 常见API2.sockaddr结构 网络编程预备知识 认识端口号 端口号(port)是传输层协议的内容. 端口号是一个2字节16位的整数; 端口号用来标识一个进程, 告诉操作系…

Linux :LNMP(源码包安装)

Linux 虚拟机 VMware&#xff1a;安装centos7_鲍海超-GNUBHCkalitarro的博客-CSDN博客 物理机 linux&#xff1a;真机安装centos linux&#xff08;突发事件&#xff1a;解决卡在安装界面&#xff09;{寻找镜像--u启制作--引导u盘--解决卡在安装界面--安装配置}_安装centos7卡…

Linux--文件/目录权限

看懂权限&#xff1a; 拥有者&#xff1a;文件属于谁 所属组&#xff1a;文件属于哪个组 other:不属于上面两种的任何一个&#xff0c;就是other 图示&#xff1a; 分析&#xff1a; ①第一列&#xff1a; d:目录 -:普通文件 p:管道文件 b:块设备 c:字符设备 l:链接…

数学建模概论

1、从现实对象到数学模型 我们常见的模型 玩具、照片......~实物模型水箱中的舰艇、风洞中的飞机......~物理模型地图、电路图、分子结构图......~符号模型 模型是为了一定目的&#xff0c;对客观事物的一部分进行简缩、抽象、提炼出来的原型的替代物&#xff0c;集中反映了原型…

【C++1】函数重载,类和对象,引用,string类,vector容器,类继承和多态,/socket

文章目录 1.函数重载&#xff1a;writetofile()&#xff0c;Ctrue和false&#xff0c;C0和非02.类和对象&#xff1a;vprintf2.1 构造函数&#xff1a;对成员变量初始化2.2 析构函数&#xff1a;一个类只有一个&#xff0c;不允许被重载 3.引用&#xff1a;C中&取地址&…

沁恒CH32V307VCT6最小系统板/开发板开源

沁恒CH32V307VCT6最小系统板&#xff0c;引出了所有IO口&#xff0c;一个Type-C连接到USB2.0全速OTG接口&#xff0c;一个Flash芯片 型号W25Q64 容量64Mbit 连接到SPI2接口&#xff0c;板上还有TL432电压基准1.25V(实测1.246V左右)可通过跳线连接到PC3的AD13&#xff0c;还有3.…