有向无权图的最短路径

news2024/11/25 23:03:43

在运筹学领域的经典模型中,最大流问题、多商品网络流问题和最短路径问题等都依附在图上对问题进行描述,同样,当我们梳理问题的数学模型,或理解相关问题的求解算法时,也要依靠它。因此,我将总结和图相关的问题,并梳理各类问题的求解算法。本文先对寻找有向无权图最短路径的方法进行小结。

文章目录

    • 图的定义和种类
    • 寻找有向无权图最短路径
    • 算法实现

图的定义和种类

在这里插入图片描述

如上所示,在一个图中有两个重要的组成元素,分别是节点和边,通常我们会把节点集合记作 V = { v 1 , v 2 . . . v n } V=\lbrace v_1,v_2...v_n \rbrace V={v1,v2...vn},将边记作 E = { e 1 , e 2 , . . . e m } E=\lbrace e_1,e_2,...e_m \rbrace E={e1,e2,...em},这样就可以用数学方式表示出来一个图 G = ( V , E ) G=(V,E) G=(V,E)。图中的节点可以表示实际生活中的对象,节点之间的边可以理解为对象之间的特定关系;比如,可以将上图中每个节点想象为每个城市的火车站,那边就可以看作两座城市的火车站之间可以通车。

有了图的基本概念后,伴随着各种各样的问题,就有了形形色色的图。假设还把它理解为城市之间的火车站连通情况,现在我想在这副图上表示出两个城市之间火车通车的时间,那我就可以给每条边加上权重,这样它就变成了无向有权图;如果车站之间是单程的(只是假定),那我就可以给每条边加上方向,这样就得到一个有向有权图 。下图是一个有向有权图。

在这里插入图片描述

寻找有向无权图最短路径

在有向无权图中,我们将相邻节点之间的路径长度定义为 1 1 1。寻找有向无权图的最短路径,即给定一个起点和终点后,找到一个从起点到终点距离最短的路径; 也可以理解为从起点经过最少数量的节点,到达终点(仅限有向无权图)。

在这里插入图片描述

求解有向无权图的最短路径算法中, 给算法输入一个图和起点,就可以得到从起点到各点的最短路径。接下来以上图为例,讲述算法的求解过程。

初始化阶段,准备一个Queue,并初始化一个Status table,如下表所示。
在这里插入图片描述

Status table中,有四列,第一列是图中的节点名称,第二列表示该节点有没有被访问过,初始时所有节点设置为 n o no no,第三列 d i s t dist dist表示该节点与起点之间的距离,初始时设置为 i n f inf inf,即为无限远;第四列为该节点在最优路径中对应的前置节点,初始时设置为 0 0 0

  • 初始化

我们假设起点为 v 3 v_3 v3,寻找到剩余节点的最短路径。在初始化阶段,将Status table中的起点 v 3 v_3 v3 v i s i t visit visit设置为 y e s yes yes,自己和自己的距离为 0 0 0,设置 d i s t dist dist为0。同时,将起点 v 3 v_3 v3放入Queue中。这些完成后,我们可以进行第一轮循环。

在这里插入图片描述

  • Iteration 1
    • 从队列中取出第一个节点,即 v 3 v_3 v3
    • 根据给出的图发现,节点 v 3 v_3 v3的相邻节点有 v 1 v_1 v1 v 6 v_6 v6
      • 更新 v 1 v_1 v1 v 6 v_6 v6 v i s i t visit visit y e s yes yes,代表已经被访问过。更新 d i s t dist dist为1( v 3 v_3 v3 v 1 v_1 v1的距离为1)。更新 p a t h path path v 3 v_3 v3
      • v 1 v_1 v1 v 6 v_6 v6放入Queue队列中。
    • 进行第二轮循环

在这里插入图片描述

  • Iteration 2

    • 从队列中取出第一个节点,即 v 1 v_1 v1

    • 根据给出的图发现,节点 v 1 v_1 v1的相邻节点有 v 2 v_2 v2 v 4 v_4 v4

      • 更新 v 2 v_2 v2 v 4 v_4 v4 v i s i t visit visit y e s yes yes,代表已经被访问过。更新 d i s t dist dist1+1( v 3 v_3 v3 v 1 v_1 v1的距离为1, v 1 v_1 v1 v 2 v_2 v2的距离为1,因此为2)。更新 p a t h path path v 1 v_1 v1
      • v 2 v_2 v2 v 4 v_4 v4放入Queue队列中。
    • 进行第三轮循环

在这里插入图片描述

  • Iteration 3

    • 从队列中取出第一个节点,即 v 6 v_6 v6

    • 根据给出的图发现,节点 v 6 v_6 v6没有相邻节点。不做操作。

    • 进行第四轮循环

在这里插入图片描述

  • Iteration 4

    • 从队列中取出第一个节点,即 v 2 v_2 v2

    • 根据给出的图发现,节点 v 2 v_2 v2的相邻节点有 v 4 v_4 v4 v 5 v_5 v5

      • 由于 v 4 v_4 v4已经被访问过了,不需要更新
      • 更新 v 5 v_5 v5 v i s i t visit visit y e s yes yes,代表已经被访问过。更新 d i s t dist dist2+1。更新 p a t h path path v 2 v_2 v2
      • v 5 v_5 v5放入Queue队列中。
    • 进行五轮循环

在这里插入图片描述

  • Iteration 5

    • 从队列中取出第一个节点,即 v 4 v_4 v4

    • 根据给出的图发现,节点 v 4 v_4 v4的相邻节点有 v 3 v_3 v3 v 5 v_5 v5 v 6 v_6 v6 v 7 v_7 v7

      • 由于 v 3 v_3 v3 v 5 v_5 v5 v 6 v_6 v6已经被访问过了,不需要更新
      • 更新 v 7 v_7 v7 v i s i t visit visit y e s yes yes,代表已经被访问过。更新 d i s t dist dist2+1。更新 p a t h path path v 4 v_4 v4
      • v 7 v_7 v7放入Queue队列中。
    • 判断Queue是否为空,若不为空,进行第六轮循环,若为空,则结束,Status table中记录了起点到其他所有节点的最短距离和路径。

在这里插入图片描述

  • Iteration 6

    • 从队列中取出第一个节点,即 v 5 v_5 v5

    • 根据给出的图发现,节点 v 5 v_5 v5的相邻节点有 v 7 v_7 v7

      • 由于 v 7 v_7 v7已经被访问过了,不需要更新
    • 判断Queue是否为空,若不为空,进行第七轮循环,若为空,则结束,Status table中记录了起点到其他所有节点的最短距离和路径。

在这里插入图片描述

  • Iteration 7

    • 从队列中取出第一个节点,即 v 7 v_7 v7

    • 根据给出的图发现,节点 v 7 v_7 v7的相邻节点有 v 6 v_6 v6

      • 由于 v 6 v_6 v6已经被访问过了,不需要更新
    • 判断Queue是否为空,若不为空,进行第八轮循环,若为空,则结束,Status table中记录了起点到其他所有节点的最短距离和路径。已经为空,结束。

在这里插入图片描述

通过上面的手算,我们理清了寻找有向无权图最短路径的算法步骤,有几个需要注意的地方用加粗标识出来。

算法实现

沿着上面的求解思路,我们不难得出,算法的结束标志就是队列是否为空,若为空,则代表算法结束。下面是我用python求解的代码,给一个图、起点和终点,得到最优路径。

import networkx as nx
import matplotlib.pyplot as plt
import math


#-------------------------------构建有向无权图-----------------------------------
# 创建无权有向图
Graph = nx.DiGraph()
# 添加节点
Graph.add_nodes_from(['v1', 'v2', 'v3', 'v4', 'v5', 'v6', 'v7'])
# 添加边
Graph.add_edges_from([
    ('v1', 'v2'),
    ('v1', 'v4'),
    ('v2', 'v4'),
    ('v2', 'v5'),
    ('v3', 'v1'),
    ('v3', 'v6'),
    ('v4', 'v3'),
    ('v4', 'v5'),
    ('v4', 'v6'),
    ('v4', 'v7'),
    ('v5', 'v7'),
    ('v7', 'v6'),
])
# 获取节点和边的数量
# print(list(Graph.edges))
# print(list(Graph.nodes))
# 画图
# nx.draw(Graph, pos=nx.planar_layout(Graph), with_labels=True)
# plt.show()



#-------------------------------寻找最短路径-----------------------------------
def find_shortest_path(Graph, begin_node, end_node):
    # 1. 初始化列表,用来存放节点
    select_nodes        = []
    neighboring_nodes   = []
    # 2. 初始化键值为列表的字典,存放访问信息
    total_nodes_status  = {}
    for node in Graph.nodes:
        total_nodes_status[node] = ['false', None, ' ']
        #print(total_nodes_status[node])
    # 3. 将起点插入selct_nodes
    select_nodes.append(begin_node)
    # 4. 更新total_nodes_status中的begin_node
    total_nodes_status[begin_node][0] = 'true'
    total_nodes_status[begin_node][1] = 0
    #print(total_nodes_status)
    # 5. 当select_nodes不为空时执行如下循环操作
    while(len(select_nodes)!=0):
        # 5.1 取出队列顶端的节点
        current_node = select_nodes[0]
        select_nodes.remove(current_node)
        # 5.2 找到current_node相邻的节点, 若该节点没有被访问过, 将其放入neighboring_nodes
        neighboring_nodes.clear()
        for node in Graph.neighbors(current_node):
            if total_nodes_status[node][0] == 'false':
                neighboring_nodes.append(node)
        # 5.3 对于neighboring_nodes中的所有节点,做下面操作
        for node in neighboring_nodes:
            # 5.3.1 将node插入select_nodes
            select_nodes.append(node)
            # 5.3.2 更新total_nodes_status中node被访问过
            total_nodes_status[node][0] = 'true'
            # 5.3.3 更新total_nodes_status中node的距离
            total_nodes_status[node][1] = total_nodes_status[current_node][1] + 1
            # 5.3.4 设置total_nodes_status中node的前置节点
            total_nodes_status[node][2] = current_node
    # 6. 若为空, 则执行完毕, 输出最终的状态
    for key, value in total_nodes_status.items():
        print(key, value)

    # 7.记录起点到终点的最优路径
    shortest_path = []
    shortest_path_length = total_nodes_status[end_node][1]
    current_node = end_node
    while current_node != begin_node:
        shortest_path.append(current_node)
        current_node            = total_nodes_status[current_node][2]
    shortest_path.append(begin_node)
    shortest_path.reverse()

    return shortest_path_length, shortest_path



#-------------------------------算例测试-----------------------------------
begin_node 	= 'v3'
end_node 	= 'v7'
shortest_path_length, shortest_path = find_shortest_path(Graph, begin_node, end_node)
print("起点%s到终点%s的最短距离是:%g \n "
      "路线如下所示:" % (begin_node, end_node, shortest_path_length))
path = ""
for element in shortest_path:
    if(element != end_node):
        path    += element
        path    += " -> "
    else:
        path    += element

print(path)


当我输入起点 v 3 v_3 v3和终点 v 7 v_7 v7时,运行算法,最终的Status table和最优路径如图所示:
在这里插入图片描述

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

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

相关文章

Android NDK JNI 开发native层崩溃日志栈分析

问题: 在Android的JNI开发中,你是否看到如下一堆崩溃日志,不知如何下手分析问题,崩溃在哪一行? 11-16 17:20:44.844 23077 23077 W test_jni_h: jni_preload: Starting for processln 11-16 17:20:44.844 23077 2307…

AWD比赛中的一些防护思路技巧

## 思路1: 1、改服务器密码 (1)linux:passwd (2)如果是root删除可登录用户:cat /etc/passwd | grep bash userdel -r 用户名 (3)mysql:update mysql.user set…

基于SpringBoot+Vue的二手物品交易平台

基于SpringBootVue的二手物品交易平台的设计与实现~ 开发语言:Java数据库:MySQL技术:SpringBootMyBatisVue工具:IDEA/Ecilpse、Navicat、Maven 系统展示 主页 详情 管理员界面 摘要 本项目是基于Spring Boot 和 Vue 技术栈构建…

latex简单使用

​​文章目录 公式详解 普通公式公式居中带标号公式上标下标根号分式括号运算符列表 无序列表有序列表插入图片 单图多图排版表格脚注与定理子标题目录与附录 目录附录参考文献字体设置 字体样式 加粗斜体字母大写等线自定义字体字体大小 第一种设置第二种设置第三种设置 页面…

系列六、JVM的内存结构【栈】

一、产生背景 由于跨平台性的设计,Java的指令都是根据栈来设计的,不同平台的CPU架构不同,所以不能设计为基于寄存器的。 二、概述 栈也叫栈内存,主管Java程序的运行,是在线程创建时创建,线程销毁时销毁&…

【PyQt小知识 - 4】:QGroupBox分组框控件 - 边框和标题设置

QGroupBox QGroupBox 是 PyQt 中的一个小部件,用于创建一个带有标题的组框。 可以使用 QGroupBox 将相关控件分组并添加一个标题。 以下是一个使用 QGroupBox 的示例代码(示例一): from PyQt5.QtWidgets import * import sysa…

ERP管理系统:企业升级的秘密武器

ERP管理系统:企业升级的秘密武器 在当今快速发展的商业环境中,企业要想保持竞争力,就必须不断进行自我升级。而在这个过程中,ERP管理系统以其强大的功能和优化流程的能力,逐渐成为了企业升级的秘密武器。 一、ERP管理…

Unity开发之C#基础-异常处理(Try Catch)

前言 其实本来这章应该将栈和队列的 但是后来想想 栈和队列在实际应用很少跟多的是大家了解一下栈和队列的基本常识比如先进先出的是谁后进先出的是谁这种 csdn有很多介绍栈和队列的文章 我觉得都比我理解深刻所以大家可以去搜索参照一下 今天我们继续往下讲解 如何自己主动的…

【Java 进阶篇】JQuery 遍历 —— For 循环的奇妙之旅

在前端开发的世界里,遍历是一个常见而重要的操作。它让我们能够浏览并操纵文档中的元素,为用户提供更加丰富和交互性的体验。而在 JQuery 中,遍历的方式多种多样,其中 for 循环是一种简单而灵活的选择。在本篇博客中,我…

11-Vue基础之组件通信(二)

个人名片: 😊作者简介:一名大二在校生 🤡 个人主页:坠入暮云间x 🐼座右铭:懒惰受到的惩罚不仅仅是自己的失败,还有别人的成功。 🎅**学习目标: 坚持每一次的学习打卡 文章…

Freeswitch中mod_commonds

mod_commands Table of Contents (click to expand) 0. About1. Usage 1.1 CLI1.2 API/Event Interfaces1.3 Scripting Interfaces1.4 From the Dialplan2. Format of returned data3. Core Commands 3.1 acl  3.1.1 Syntax3.1.2 Examples3.2 alias 3.2.1 Syntax3.2.2…

作为HR是看重学历还是工作经验?

作为HR是看重学历还是工作经验? 这个没有绝对的统一的看法,如果我是HR我更看重工作经验,如果是中小企业,对于人才嘛,那肯定是要到岗就能干活的,底子好不好先不说,关键是要能干活的。 不过近些…

【算法基础】分解质因数

文章目录 什么是分解质因数具体案例输入格式输出格式数据范围 原理讲解原始方法转换思路利用试除法判定质数的思路为什么不需要单独判断是否为质数 什么是分解质因数 分解质因数是指将一个合数用质因数相乘的形式表示出来,即将一个合数分解为若干个质数的乘积。其中…

人工智能如何重塑体验为先的汽车行业

面向汽车行业用户体验的 AI 人工智能的影响力继续在各个主要行业中迅速蔓延,全球各地的公司都开始大力投资 AI 技术,以提高自身的竞争优势。未来的趋势表明,企业如果不立即采用人工智能战略,就可能会远远落后于竞争对手。 AI 和…

es head 新增字段、修改字段、批量修改字段、删除字段、删除数据、批量删除数据

目录 一、新增字段 二、修改字段值 三、批量修改字段值 ​四、删除字段 五、删除数据/文档 六、批量删除数据/文档 一、新增字段 put http://{ip}:{port}/{index}/_mapping/{type} 其中,index是es索引、type是类型 数据: {"_doc"…

一阶滤波器(一阶巴特沃斯滤波器)

连续传递函数G(s) 离散传递函数G(z) 转换为差分方程形式 一阶巴特沃斯滤波器Filter Designer参数设计:参考之前的博客Matlab的Filter Designer工具设计二阶低通滤波器 设计采样频率100Hz,截止频率20Hz。 注意:设计参数使用在离散系统中&…

【邻接表,图的邻接表存储表示】

文章目录 邻接表无向图有向图图的邻接表存储表示:图的邻接表的弧(边)的结点结构 邻接矩阵的好处: 1.直观,简单,好理解。 2.方便检查任意一对顶点间是否存在边 3.方便找到任一顶点的所有“邻接点”&#xff…

【MATLAB源码-第80期】基于蚯蚓优化算法(EOA)的无人机三维路径规划,输出做短路径图和适应度曲线

操作环境: MATLAB 2022a 1、算法描述 蚯蚓优化算法(Earthworm Optimisation Algorithm, EOA)是一种启发式算法,灵感来源于蚯蚓在自然界中的行为模式。蚯蚓优化算法主要模仿了蚯蚓在寻找食物和逃避天敌时的行为策略。以下是蚯蚓…

为什么LDO一般不用在大电流场景?

首先了解一下LDO是什么? LDO(low dropout regulator,低压差线性稳压器)或者低压降稳压器,它的典型特性就是压降。 那么什么是压降? 压降电压 VDO 是指为实现正常稳压,输入电压 VIN 必须高出 所…

C++基础(3)——类与对象

1.构造函数: 1.1 构造函数的引入: 在关于数据结构这一部分的文章中,创建了一个新的数据结构后,通常需要编写一个初始化函数来对这个数据结构进行一次初始化。在C的类中,如果存在函数,同样也需要对函数进行…