dijkstra 算法_图的最短路径Dijkstra算法

图里面有一种经典问题,叫做最短路径问题。现实场景也非常的多,比如,你想从A地到D地,但有很多条路径可以到达,中间要经过多次的中转,每一段路花费的时间都不一样,那么找到一条路径,让从A到D花费的时间最短,就是一个最短路径问题。

抽象一下就是一个有权图中,找到连通两个点权重最小的那条路径。如下图,如果想要知道A点到D点权值最短的路径,就是个最短路径问题。

03160e3d86ead3a0c8cd0d28fec588c8.png

迪杰斯特拉(Dijkstra)算法就是解决这个问题的经典算法。

他的思路就是通过遍历所有的节点,不停的更新初始节点到每一个节点的最短距离。

这么说有点抽象,那就举个例子。首先选定一个初始点,假设是A,然后开始遍历。A到A的距离是0,开始遍历下一个节点。B节点,那A-B直接最短距离就是10,再遍历和A相邻的另一个节点F,A-F最短距离为11。这样相当于就遍历了A相邻的第一层节点。

然后遍历距离A最近的那个节点周围的节点。从目前来看A-B=10,A-F=11,所以从B开始。B的相邻节点C,A-B-C的距离为(A-B)+(B-C)= 28,同理可以算出来A-B-I = 22,A-B-G = 26。

然后再从距离A最近的节点开始计算,这时候比较的就是F(11),C(28),I(22),G(26)几个节点。发现F最近,于是再从F开始计算。F周围的节点发现又见到了G了,目前A到I的最短路径是A-B-G=26。我们就需要比较一下新的路径有没有更近一些,需不需要更新最短路径。然后发现A-F-G=11+17=28>26,并没有更近。所以A到G的最短路径仍然保持不变是A-B-G。

用相同的方式,每次都遍历距离A最近的那个节点,而距离的远近是根据更新到目前为止到达该节点的最短路径。

熟悉图遍历的话,发现这种遍历方法不是深度优先,也不算是广度优先,可以称之为“距离”优先的遍历。而如果把这里面所有节点之间的距离设置成为1,就变成了一个无权图,Dijkstra就退化成为了广度优先遍历算法。

有了这个认知,其实稍微改造一下广度优先遍历的算法,就可以得到Dijkstra最短路径算法啦。

需要改变的就是要把广度优先遍历中使用的队列,变成一个“优先队列”。原来的队列是先进先出,而“优先队列”是根据权重,也就是Dijkstra算法中的距离,排序,权重小的先出。

具体的代码实现如下

def sp_dijkstra(graph,start_vertex_index):    # 初始化前置结点列表    p = list(None for i in range(len(graph.vertex_list)))    # 初始化最短距离列表    d = list(None for i in range(len(graph.vertex_list)))    # 初始化优先队列    q = queue.PriorityQueue()    # 初始化起始结点,起始结点到本身的距离为0    d[start_vertex_index] = 0    # 初始化起始结点的前置结点,为本身    p[start_vertex_index] = start_vertex_index    # 将初始结点和与初始结点的距离放入优先队列,初始节点距离为0    q.put((0,(graph.vertex_list[start_vertex_index],start_vertex_index)))    # 记录节点是否已经被计算最短路径    v_short = []    # 开始处理    while not q.empty():        v,i = q.get()[1]        if v_short.__contains__(i):            continue        else:            v_short.append(i)        # 遍历v的相邻结点        e = v.firstedge        while e is not None:            # 初始节点通过i节点到达e节点距离            temp_d = d[i] + e.weight            # 更新最短距离            if (d[e.adjvex] is None) or (d[e.adjvex] > temp_d)  :                d[e.adjvex] = temp_d                p[e.adjvex] = i            q.put((d[e.adjvex],(graph.vertex_list[e.adjvex],e.adjvex)))            e = e.nxt    print('最短距离:',d)    print('前置结点:',p)

尝试求一下上面那张图,从A点出发的最短路径。

graph_matrix = np.array([[0, 10, 0, 0, 0, 11, 0, 0, 0],                         [10, 0, 18, 0, 0, 0, 16, 0, 12],                         [0, 18, 0, 22, 0, 0, 0, 0, 8],                         [0, 0, 22, 0, 20, 0, 24, 16, 21],                         [0, 0, 0, 20, 0, 26, 0, 7, 0],                         [11, 0, 0, 0, 26, 0, 17, 0, 0],                         [0, 16, 0, 24, 0, 17, 0, 19, 0],                         [0, 0, 0, 16, 7, 0, 19, 0, 0],                         [0, 12, 8, 21, 0, 0, 0, 0, 0]])vex_list = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I']g = graph.Adgraph()g.build(vex_list, graph_matrix)sp_dijkstra(g,0)

结果如下

节点: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I']最短距离:[0, 10, 28, 43, 37, 11, 26, 44, 22]前置结点:[0, 0, 1, 8, 5, 0, 1, 4, 1]

END

30abc000e9ec48c9d48e128696349162.png

作者:锅哥不姓郭

   图片:网络(侵删)

81ecac94f349f0d07efedd7e353dc842.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值