Python解决最短路径问题—Dijkstra算法+堆优化

Python解决最短路径问题—Dijkstra算法+堆优化

  • 最短路问题介绍
  • Dijkstra算法介绍
    • 算法思想
    • Dijkstra算法步骤
  • 代码

宇智波一打七今天学习了一个新算法, 迫不及待和大家分享一下

最短路问题介绍

在一个赋权图中,从一个顶点出发到达另外一个顶点的路的权和最小的路径,称为最短路径。Dijkstra算法是解决这一问题的经典算法,接下来我们介绍如何用python实现这一算法。

Dijkstra算法介绍

Dijkstra算法由Dijkstra在1959年发现,这个算法不仅找到了最短的 ( u 0 , v 0 ) (u_0,v_0) (u0,v0) 路,而且给出了从 u 0 u_0 u0 到其他所有顶点的最短路。

算法思想

假设 S S S V V V 的真子集且有 u 0 ∈ S u_0\in S u0S,记 S ‾ \overline{S} S V − S V-S VS。若 p a t h = u 0 ⋯ u v ‾ path=u_0\cdots \overline{uv} path=u0uv,这里 u ‾ ∈ S \overline{u} \in S uS ( u 0 , u ‾ ) (u_0,\overline{u}) (u0,u) 一定是最短路。所以


d ( u 0 , v ‾ ) = d ( u 0 , u ‾ ) + w ( u v ‾ ) d(u_0,\overline{v})=d(u_0,\overline{u})+w(\overline{uv}) d(u0,v)=d(u0,u)+w(uv)

u 0 u_0 u0 S ‾ \overline{S} S 距离为:


d ( u 0 , S ‾ ) = min ⁡ { d ( u 0 , u ) + w ( u v ) } d(u_0,\overline{S})=\min \{d(u_0,u)+w(uv)\} d(u0,S)=min{d(u0,u)+w(uv)}

这里 u ∈ S , v ∈ S ‾ u\in S,v\in \overline{S} uS,vS。那么我们可以思考,我们可以从集合 { u 0 } \{u_0\} {u0} 开始构造一个递增序列 S 1 , S 2 , ⋯   , S v − 1 S_1,S_2,\cdots ,S_{v-1} S1,S2,,Sv1,这样我们便可以得到由 u 0 u_0 u0 S i S_i Si 的所有顶点的最短路。

Dijkstra算法步骤

  1. S 0 = { u 0 } , d i s ( u 0 ) = 0 S_0=\{u_0\},dis(u_0)=0 S0={u0},dis(u0)=0,对 v ≠ u 0 , d i s ( v ) = ∞ v\not=u_0,dis(v)=\infty v=u0,dis(v)= i = 0 i=0 i=0.
  2. 对每个 v ∈ S i ‾ v\in \overline{S_i} vSi,令 d i s ( v ) = min ⁡ { d i s ( v ) , d i s ( u i ) + d i s ( u i v ) } dis(v)=\min \{dis(v),dis(u_i)+dis(u_iv)\} dis(v)=min{dis(v),dis(ui)+dis(uiv)},最小的 v v v 记为 u i + 1 u_{i+1} ui+1,更新 S i + 1 = S i ∪ { u i + 1 } S_{i+1}=S_i\cup \{u_{i+1}\} Si+1=Si{ui+1}.
  3. i = ∣ V ∣ − 1 i=|V|-1 i=V1 ,停止。若 i < ∣ V ∣ − 1 i<|V|-1 i<V1 ,则 i = i + 1 i=i+1 i=i+1,返回第 2 2 2步。

代码

优美的代码来了

import heapq
class graph:
    def __init__(self,num,ma):
        self.edge={}
        self.dic=[ma]*(num+1)
    def add(self,u,v,w):
        if u in self.edge:
            self.edge[u].append((v,w))
        else:
            self.edge[u]=[(v,w)]
    def dijkstra(self,start):
        dis=self.dic
        dis[start]=0
        heap=[]
        visit=[0 for i in range(len(dis))]
        for i in self.edge[start]:
            dis[i[0]]=i[1]
            heapq.heappush(heap,(i[1],i[0]))# i[1]为该边权值,i[0]为该点,从start点到其余点的边入堆
        while heap!=[]:
            vic=heapq.heappop(heap)# 弹出最小边
            if visit[vic[1]]==1:# 如果该点已经弹出过,则不再松弛
                continue
            visit[vic[1]] = 1# 记录弹出点
            if vic[1] not in self.edge:# 防止有向图中出度为0的点
                continue
            for i in self.edge[vic[1]]:
                if dis[i[0]]>dis[vic[1]]+i[1]:# 判断是否松弛
                    dis[i[0]]=dis[vic[1]]+i[1]# 松弛边
                    heapq.heappush(heap,(i[1],i[0]))# 松弛过边则把新边权值入堆
        self.dic=dis
    def printf(self):
        print(self.dic[1:])
if __name__ == "__main__":
    n,m=6,8
    nums = [[1,3,10],[1,5,30],[1,6,100],[2,3,5],[3,4,50],[4,6,10],[5,6,60],[5,4,20]]
    g=graph(n,1000000000000)
    for i in nums:
        g.add(i[0],i[1],i[2])
    g.dijkstra(1)
    g.printf()

我们在算法第二步求最小的点 u i + 1 u_{i+1} ui+1时可用堆做优化。在python中有模块heapq,这里我们直接使用该模块的最小堆即可。
注意:dijkstra算法不能求解有负权边地图(思考一下为什么?)。

你可能感兴趣的:(Python解决最短路径问题—Dijkstra算法+堆优化)