Dijkstra算法在Python上的研究,以及如何修改算法以输出多条最短路径

一、在一个无向图中使用dijkstra算法,输出一个已知点所求点的最小距离,以及输出到该点的路径。

以该图为例,从点A出发,求到某点的最短路径和经过的点名称。

python代码如下,

def DFS(dic:dict,node:str,graph): # 在图中深搜dijkstra算出的结果
    if node == 'A': # 当遍历到节点A时,返回结果
        return
    stack.append(dic[node][0])
    DFS(dic,stack[-1],graph)


class Mintree(object):
    def creat_graph(self, graph, vertex: int, data: [], weight: []):      # graph:图类   vertex: 顶点数  data: 顶点名字  weight:边权值
        for i in range(vertex):
            graph.data[i] = data[i]
            for j in range(vertex):
                graph.weight[i][j] = weight[i][j]

    def dijkstra(self, graph,s:str): # s为要求的点
        index = graph.data.index(s) # 索引该点在结果中的位置
        re = {}
        result = graph.weight[0] # 最终结果
        result1 = []   # 每行的数据(待处理的)
        Min_name = [0] # 用于添加每行最小值的序号
        a = 0 # 每行最小值的序号
        for p in range(1,graph.vertex):
            if result[p] < 10000:
                re[graph.data[p]] = ['A']
            else:
                re[graph.data[p]] = []
        for i in range(graph.vertex-2):
            result1 = graph.weight[a].copy() # 添加每一行数据
            for m in range(len(Min_name)):
                result1[Min_name[m]] = 10000 # 将已松弛的点记为无穷,即不再选取此点为最小值
            result1[i] = 10000 # 将该点到自身的距离记为无穷,因为考虑到自身的距离为0无意义
            minimum = min(result1)
            for j in range(graph.vertex):
                if result1[j] == minimum:
                    a = j
                    Min_name.append(a) # 找到处理完后每行的最小值,并记录该点,准备进行下一步松弛
            for k in range(graph.vertex): # 最重要的一步:松弛; 即确定一个点后,将该点到其余点的距离是否能够缩短,并记录
                if (graph.weight[a][k] + result[a]) < result[k]:
                    result[k] = graph.weight[a][k] + result[a]
                    re[graph.data[k]] = [graph.data[a]] # 建立一个字典,存储每个节点到起点最近的上一个点
        print(result[index])
        return re


class Graph(object): #创建一个图的类
    def __init__(self, vertex):
        self.vertex = vertex
        self.data = [0] * vertex
        self.weight = [[0 for row in range(vertex)]for col in range(vertex)]

if __name__ == '__main__':
    vertex_data = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']
    weights = [[0, 4, 10000, 10000, 7, 10000, 11, 10000],
              [4, 0, 2, 10000, 6, 10000, 10000, 10000],
              [10000, 2, 0, 9, 1, 8, 10000, 10000],
              [10000, 10000, 9, 0, 10000, 1, 10000, 3],
              [7, 6, 1, 10000, 0, 10, 9, 4],
              [10000, 10000, 8, 1, 10, 0, 10000, 2],
              [11, 10000, 10000, 10000, 9, 10000, 0, 5],
              [10000, 10000, 10000, 3, 4, 2, 5, 0]]
    length = len(vertex_data)
    g = Graph(length)
    tree = Mintree()
    tree.creat_graph(g, length, vertex_data, weights)
    res = tree.dijkstra(g,'D')
    print(res)
    stack = ['D']
    DFS(res,'D',g)
    print(stack[::-1]) # 输出到该点的路径

具体的松弛思想在网上可以找到完整的解释,在此就不赘述了,本文只记录思路及在python上面的解决方案。

二、若有多条最短路,如何输出所有最短路径

python代码如下

def DFS(dic:dict,node:str,graph):
    fl = 0
    if node == 'D':  # 从起点开始,寻找终点,到终点(此处为’D‘)截止并回溯
        print(['A'] + stack) # 加上起点,完整输出,并且只输出成功到达终点的过程
        stack.pop(-1) # 退出栈中的终点,继续递归
        return
    for i in graph.data[1:]:  # 遍历所有节点,找到可行的节点并继续递归
        if node in dic[i]:
            stack.append(i)
            fl = 1
            DFS(dic,stack[-1],graph)
    if fl == 0:
        stack.pop(-1) # 若下一个对应点不在最短路径中,即最终结果不含该点,则使其出栈
    stack.clear()  # 这一行代码就想了半天,意为:每次递归结束清除栈中所有点,即可清楚冗余的递归过程,只记录成功到达终点的一次递归
class Mintree(object):
    def creat_graph(self, graph, vertex: int, data: [], weight: []):      # graph:图类   vertex: 顶点数  data: 顶点名字  weight:边权值
        for i in range(vertex):
            graph.data[i] = data[i]
            for j in range(vertex):
                graph.weight[i][j] = weight[i][j]

    def dijkstra(self, graph, s:str): # s为所求点
        index = graph.data.index(s) # 索引该点
        re = {}
        result = graph.weight[0] # 最终结果
        result1 = []   # 每行的数据(待处理的)
        Min_name = [0] # 用于添加每行最小值的序号
        a = 0 # 每行最小值的序号
        for p in range(1,graph.vertex):
            if result[p] < 10000:
                re[graph.data[p]] = ['A']
            else:
                re[graph.data[p]] = []
        for i in range(graph.vertex-2):
            result1 = graph.weight[a].copy() # 添加每一行数据
            for m in range(len(Min_name)):
                result1[Min_name[m]] = 10000 # 将已松弛的点记为无穷,即不再选取此点为最小值
            result1[i] = 10000 # 将该点到自身的距离记为无穷,因为考虑到自身的距离为0无意义
            minimum = min(result1)
            for j in range(graph.vertex):
                if result1[j] == minimum:
                    a = j
                    Min_name.append(a) # 找到处理完后每行的最小值,并记录该点,准备进行下一步松弛
            for k in range(graph.vertex): # 最重要的一步:松弛; 即确定一个点后,将该点到其余点的距离是否能够缩短,并记录
                if (graph.weight[a][k] + result[a]) < result[k]:
                    result[k] = graph.weight[a][k] + result[a]
                    re[graph.data[k]] = [graph.data[a]]
                elif (graph.weight[a][k] + result[a]) == result[k]: # 建立一个字典,存储每个节点到起点最近的上一个点,此处若有相等的距离也存储,因此用字典加列表
                    if graph.weight[a][k] != 0:
                        re[graph.data[k]].append(graph.data[a])
        print(result[index])
        return re


class Graph(object):
    def __init__(self, vertex):
        self.vertex = vertex
        self.data = [0] * vertex
        self.weight = [[0 for row in range(vertex)]for col in range(vertex)]

if __name__ == '__main__':
    vertex_data = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']
    weights = [[0, 4, 10000, 10000, 7, 10000, 11, 10000],
              [4, 0, 2, 10000, 6, 10000, 10000, 10000],
              [10000, 2, 0, 9, 1, 8, 10000, 10000],
              [10000, 10000, 9, 0, 10000, 1, 10000, 3],
              [7, 6, 1, 10000, 0, 10, 9, 4],
              [10000, 10000, 8, 1, 10, 0, 10000, 2],
              [11, 10000, 10000, 10000, 9, 10000, 0, 5],
              [10000, 10000, 10000, 3, 4, 2, 5, 0]]
    length = len(vertex_data)
    g = Graph(length)
    tree = Mintree()
    tree.creat_graph(g, length, vertex_data, weights)
    res = tree.dijkstra(g,'D')
    print(res)
    stack = []
    DFS(res,'A',g)

过程都写在注释里了,可以看代码并理解一下。

三、反思

其实,如果看了(二)的代码,就能发现:如果去掉(二)中的这三行代码:

elif (graph.weight[a][k] + result[a]) == result[k]:
    if graph.weight[a][k] != 0:
        re[graph.data[k]].append(graph.data[a])

一样可以做普通的最短路输出(只输出一组)。但是我(一)中代码却并不是按照这个思路去写的。一开始做只输出一组最短路问题,可以从起点往终点搜索。但是,如果记录多组点

stack.append(dic[node][0])

该行代码中的索引过程就无法实现了,而且按照我写的数据结构(值为列表的字典),似乎无法实现从起点向终点的搜索。

因此经过多日的苦思冥想(悲),将搜索过程改为从终点向起点DFS,便终于完成了这项任务。

30行的dijkstra主程序想了三天,14行的DFS过程更是想了一个星期!感慨一下。

可是我觉得,输出多条最短路的算法相比于普通的输出唯一最短路的问题,实际应用广泛得多,因为它可以在最短距离的基础上考虑更多其他要素,因此给人提供了更多的选择,更多的可能性。

其中的数据结构和算法,如果还有值得推敲的地方,可以评论探讨。

  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Dijkstra算法是一种用于求解最路径问题的经典算法。在Python中实现Dijkstra算法可以通过以下步骤来完成: 1. 创建一个用于存储图的数据结构,可以使用邻接矩阵或邻接表表示。邻接矩阵是一个二维数组,用于表示各个节点之间的连接关系。邻接表是一个字典,其中每个键表示一个节点,对应的值是一个列表,表示与该节点直接相连的节点及其权重。 2. 初始化距离列表dist和已访问节点集合visited。将起始节点的距离设为0,其他节点的距离设为无穷大。 3. 重复以下步骤,直到所有节点都被访问过: a. 从未访问的节点中选择距离最小的节点u,将其标记为已访问。 b. 遍历节点u的所有邻居节点v,计算从起始节点到v的距离。如果通过u到达v的距离比当前已知的最距离要小,则更新距离列表dist和前驱节点列表prev。 4. 最终得到的距离列表dist即为起始节点到其他所有节点的最路径。通过遍历前驱节点列表prev,可以找到起始节点到任意节点的最路径。 需要注意的是,在实现Dijkstra算法时,可以使用优先队列来提高算法的效率,以快速选择距离最小的节点。 引用中提供了Python实现Dijkstra算法的具体示例代码,可以参考该代码来实现该算法。同时,引用提到了对创建地图时的问题进行优化的建议,可以忽略路径第一个数来得到正确的结果。引用和引用分别介绍了Dijkstra算法的基本思想和步骤,可以帮助理解算法的原理。<span class="em">1</span><span class="em">2</span><span class="em">3</span><span class="em">4</span>

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值