首页 > 编程语言 >最短路径算法综述:原理、比较与实现

最短路径算法综述:原理、比较与实现

时间:2024-11-10 12:16:02浏览次数:3  
标签:distances 综述 复杂度 vertices 最短 算法 num 节点

最短路径算法综述:原理、比较与实现

一、引言

在图论和计算机科学领域,最短路径问题是一个经典且重要的研究内容。它在交通导航、网络路由、物流规划等众多实际应用场景中有着广泛的应用。本文将详细介绍几种常见的最短路径算法,包括 Dijkstra 算法、Bellman - Ford 算法、Floyd - Warshall 算法和 A*算法,并从时间复杂度、空间复杂度、适用场景等方面对它们进行比较,同时给出相应的代码实现。

二、常见最短路径算法

(一)Dijkstra 算法

  1. 算法原理
    Dijkstra 算法是一种贪心算法,用于计算一个节点到其他所有节点的最短路径。它维护一个集合,集合中的节点是已经找到最短路径的节点。算法从起始节点开始,每次选择距离起始节点最近且尚未处理的节点,将其加入集合,并更新其相邻节点的距离。这个过程不断重复,直到所有节点都被处理。
  2. 代码实现(Python)
    以下是使用 Python 实现的 Dijkstra 算法示例,这里使用邻接矩阵来表示图:
import sys

# 实现 Dijkstra 算法
def dijkstra(graph, start):
    num_vertices = len(graph)
    distances = [sys.maxsize] * num_vertices
    distances[start] = 0
    visited = [False] * num_vertices
    for _ in range(num_vertices):
        min_distance = sys.maxsize
        min_vertex = -1
        for v in range(num_vertices):
            if not visited[v] and distances[v] < min_distance:
                min_distance = distances[v]
                min_vertex = v
        visited[min_vertex] = True
        for v in range(num_vertices):
            if graph[min_vertex][v] > 0 and not visited[v]:
                new_distance = distances[min_vertex] + graph[min_vertex][v]
                if new_distance < distances[v]:
                    distances[v] = new_distance
    return distances

(二)Bellman - Ford 算法

  1. 算法原理
    Bellman - Ford 算法可以处理含有负权边的图,它通过对所有边进行多次松弛操作来找到最短路径。算法的基本思想是,对于图中的每条边,如果从源节点到某个节点的路径经过这条边可以使距离更短,则更新该节点的距离。总共需要进行 V − 1 V - 1 V−1 次迭代( V V V 是图中节点的数量),然后再检查是否存在负权回路。
  2. 代码实现(Python)
import sys

# Bellman - Ford 算法实现
def bellman_ford(graph, start):
    num_vertices = len(graph)
    distances = [sys.maxsize] * num_vertices
    distances[start] = 0
    for _ in range(num_vertices - 1):
        for u in range(num_vertices):
            for v in range(num_vertices):
                if graph[u][v]!= sys.maxsize:
                    new_distance = distances[u] + graph[u][v]
                    if new_distance < distances[v]:
                        distances[v] = new_distance
    for u in range(num_vertices):
        for v in range(num_vertices):
            if graph[u][v]!= sys.maxsize:
                new_distance = distances[u] + graph[u][v]
                if new_distance < distances[v]:
                    print("图中存在负权回路")
                    return
    return distances

(三)Floyd - Warshall 算法

  1. 算法原理
    Floyd - Warshall 算法用于求解图中所有节点对之间的最短路径。它通过动态规划的思想,逐步考虑中间节点,更新任意两个节点之间的最短距离。算法使用一个二维数组来存储节点对之间的距离,通过三层嵌套循环,依次将每个节点作为中间节点进行更新。
  2. 代码实现(Python)
import sys

# Floyd - Warshall 算法实现
def floyd_warshall(graph):
    num_vertices = len(graph)
    dist = [[graph[i][j] for j in range(num_vertices)] for i in range(num_vertices)]
    for k in range(num_vertices):
        for i in range(num_vertices):
            for j in range(num_vertices):
                if dist[i][k]!= sys.maxsize and dist[k][j]!= sys.maxsize and dist[i][j] > dist[i][k] + dist[k][j]:
                    dist[i][j] = dist[i][k] + dist[k][j]
    return dist

(四)A*算法

  1. 算法原理
    A*算法是一种启发式搜索算法,它结合了 Dijkstra 算法的最优性保证和启发式信息来加速搜索过程。除了记录从起始节点到当前节点的实际代价 g ( n ) g(n) g(n),还使用一个启发式函数 h ( n ) h(n) h(n) 来估计从当前节点到目标节点的代价。算法选择具有最小 f ( n ) = g ( n ) + h ( n ) f(n)=g(n)+h(n) f(n)=g(n)+h(n) 值的节点进行扩展。
  2. 代码实现(Python)
    以下是一个简单的 A*算法在网格地图中的实现示例(这里假设启发式函数使用曼哈顿距离):
import heapq

# 计算曼哈顿距离
def manhattan_distance(node, goal):
    return abs(node[0] - goal[0]) + abs(node[1] - goal[1])

# A*算法实现
def a_star(graph, start, goal):
    open_list = []
    heapq.heappush(open_list, (0, start))
    came_from = {}
    g_score = {start: 0}
    f_score = {start: manhattan_distance(start, goal)}
    while open_list:
        current = heapq.heappop(open_list)[1]
        if current == goal:
            path = []
            while current in came_from:
                path.append(current)
                current = came_from[current]
            path.append(start)
            return path[::-1]
        for neighbor in graph[current]:
            tentative_g_score = g_score[current] + 1  # 假设相邻节点距离为 1
            if neighbor not in g_score or tentative_g_score < g_score[neighbor]:
                came_from[neighbor] = current
                g_score[neighbor] = tentative_g_score
                f_score[neighbor] = tentative_g_score + manhattan_distance(neighbor, goal)
                heapq.heappush(open_list, (f_score[neighbor], neighbor))
    return None

三、算法比较

(一)时间复杂度

  1. Dijkstra 算法
    当使用最小优先队列(如二叉堆)实现时,时间复杂度为 O ( ( V + E ) log ⁡ V ) O((V + E)\log V) O((V+E)logV)( V V V 是节点数, E E E 是边数)。在最坏情况下(稠密图, E = V 2 E = V^2 E=V2),时间复杂度接近 O ( V 2 log ⁡ V ) O(V^2\log V) O(V2logV)。
  2. Bellman - Ford 算法
    时间复杂度为 O ( V E ) O(VE) O(VE),因为它需要对每条边进行最多 V − 1 V - 1 V−1 次松弛操作。在稀疏图中,它可能比 Dijkstra 算法快,但在稠密图中效率较低。
  3. Floyd - Warshall 算法
    时间复杂度为 O ( V 3 ) O(V^3) O(V3),因为它有三层嵌套循环,每次都遍历所有节点。这个复杂度与边的数量无关,适用于处理稠密图和需要求解所有节点对最短路径的情况。
  4. A*算法
    时间复杂度取决于启发式函数的质量和图的结构。在最坏情况下,它的时间复杂度可能是指数级的,但在实际应用中,如果启发式函数选择得当,可以大大提高搜索效率,通常比 Dijkstra 算法更快。

(二)空间复杂度

  1. Dijkstra 算法
    使用最小优先队列实现时,空间复杂度为 O ( V ) O(V) O(V),用于存储节点的距离和标记。如果使用邻接矩阵存储图,还需要额外的 O ( V 2 ) O(V^2) O(V2) 空间。
  2. Bellman - Ford 算法
    空间复杂度为 O ( V ) O(V) O(V),主要用于存储节点的距离。同样,如果使用邻接矩阵存储图,需要额外考虑图的存储空间。
  3. Floyd - Warshall 算法
    空间复杂度为 O ( V 2 ) O(V^2) O(V2),因为需要一个二维数组来存储所有节点对之间的距离。
  4. A*算法
    空间复杂度取决于存储开放列表、已访问节点等数据结构的大小,通常为 O ( b d ) O(b^d) O(bd)( b b b 是分支因子, d d d 是搜索深度),在最坏情况下可能很高,但在实际应用中可以通过一些优化策略来控制。

(三)适用场景

  1. Dijkstra 算法
    适用于边权值非负的图,常用于求解单源最短路径问题,在网络路由算法中应用广泛。如果图是稀疏图且边权非负,Dijkstra 算法是一个高效的选择。
  2. Bellman - Ford 算法
    能够处理含有负权边的图,但不能处理负权回路。适用于一些需要考虑负权边情况的网络模型,如某些金融网络中的成本计算。
  3. Floyd - Warshall 算法
    用于求解所有节点对之间的最短路径,对于稠密图和需要全面路径信息的场景非常有用,如在交通规划系统中计算所有地点之间的最短距离。
  4. A*算法
    特别适用于有目标导向的搜索问题,如游戏中的角色路径规划、机器人导航等。它通过启发式信息可以快速找到目标方向上的最短路径,但启发式函数的设计需要一定的领域知识。

四、总结

最短路径算法在不同的应用场景中有各自的优势。Dijkstra 算法在边权非负的情况下效率较高;Bellman - Ford 算法可处理负权边但时间复杂度较高;Floyd - Warshall 算法适用于求解所有节点对最短路径;A*算法利用启发式信息在目标导向的搜索中表现出色。在实际应用中,需要根据图的性质(如是否有负权边、稀疏程度等)、问题的类型(单源或所有节点对最短路径)以及是否有启发式信息等因素来选择合适的最短路径算法。通过对这些算法的深入理解和正确应用,可以有效地解决各种实际的路径规划和网络优化问题。

标签:distances,综述,复杂度,vertices,最短,算法,num,节点
From: https://blog.csdn.net/yujitiankai/article/details/143656765

相关文章

  • 排序算法原理、应用与对比
    一、排序算法综述排序算法在计算机科学中具有至关重要的地位。在众多应用场景中,如数据库管理、搜索引擎结果排序、数据分析等,高效的排序算法能够极大地提高系统的性能和用户体验。不同类型的排序算法具有各自独特的特点和分类。从算法的稳定性来看,有些算法是稳定的,如插入排序......
  • 代码随想录算法训练营第19天|235. 二叉搜索树的最近公共祖先 ,701.二叉搜索树中的插入
    235.二叉搜索树的最近公共祖先文章链接:https://programmercarl.com/0235.二叉搜索树的最近公共祖先.html题目链接:https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-search-tree/思路:利用二叉搜索树的特性,当第一次遇到在[p,q]区间或[q,p]区间的元素的节点,则......
  • 救命啊!字节大模型算法实习岗面试居然栽在Transformer上了!!
    为什么在进行softmax之前需要对attention进行scaled(为什么除以dk的平方根)?transformer论文中的attention是ScaledDot-PorductAttention来计算keys和queries之间的关系。如下图所示:在公式一中,作者对0和K进行点积以获得注意力权重,然后这些权重用于加权平均V。但在实......
  • 仪表图像识别算法
    仪表图像识别算法基于AI的机器视觉分析识别技术,通过训练深度学习模型,使得摄像头能够像人一样“看”懂仪表盘上的数据。这些现场监控摄像头能够实时捕捉仪表盘的图像,利用AI算法自动分析并识别出仪表的示数或开关状态。这种技术不仅能够在任何时间、任何地点进行自动读表,还可以通过......
  • 100种算法【Python版】第60篇——滤波算法之粒子滤波
    本文目录1算法步骤2算法示例:多目标跟踪3算法应用:多维非线性系统状态模拟粒子滤波(ParticleFilter)是一种基于随机采样的贝叶斯滤波方法,广泛应用于动态系统的状态估计。它通过在状态空间中使用一组随机粒子(样本)来表示后验分布,从而处理非线性和非高斯的状态估计问......
  • 代码随想录算法训练营第18天| 530.二叉搜索树的最小绝对差, 501.二叉搜索树中的众数 , 2
    530.二叉搜索树的最小绝对差文章链接:https://programmercarl.com/0530.二叉搜索树的最小绝对差.html视频链接:https://www.bilibili.com/video/BV1DD4y11779/?vd_source=6cb513d59bf1f73f86d4225e9803d47b题目链接:https://leetcode.cn/problems/minimum-absolute-difference-in......
  • 计算机毕业设计Python+大模型动漫推荐系统 动漫视频推荐系统 机器学习 协同过滤推荐算
    作者简介:Java领域优质创作者、CSDN博客专家、CSDN内容合伙人、掘金特邀作者、阿里云博客专家、51CTO特邀作者、多年架构师设计经验、多年校企合作经验,被多个学校常年聘为校外企业导师,指导学生毕业设计并参与学生毕业答辩指导,有较为丰富的相关经验。期待与各位高校教师、企业......
  • 鸿蒙HarmonyOS证书算法库揭秘:设备认证的底层实现
    本文旨在深入探讨华为鸿蒙HarmonyOSNext系统(截止目前API12)的技术细节,基于实际开发实践进行总结。主要作为技术分享与交流载体,难免错漏,欢迎各位同仁提出宝贵意见和问题,以便共同进步。本文为原创内容,任何形式的转载必须注明出处及原作者。在华为鸿蒙HarmonyOS的安全体系中,证书算......
  • 二维椭圆拟合算法及推导过程
    目录1、间接平差法2、最小二乘法3、matlab案例4、案例结果5、参考链接1、间接平差法  该方法忽略了半长轴相对于xxx轴的旋转角度,需要较好的初......
  • 每日一题.设计相邻元素求和服务;暴力算法与哈希表的运用
    本题出自LeetCode每日一题3242,可以说比昨天的那道“每日抑题”简单不少呀,就是代码长一点,同时本题目使用了两种不同的方法,并对每一种用法进行了深度解析。本文全长5000字。题目 给你一个 nxn 的二维数组 grid,它包含范围 [0,n2-1] 内的不重复元素。实现 neighbo......