贪心算法之应用-单源最短路径-Dijkstra算法学习

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、Dijkstra算法简介
  • 二、Dijkstra 算法有关概念
  • 三、Dijkstra 算法设计思想
  • 四、Dijkstra伪码
  • 五、算法运行实例
  • 六、完整代码
  • 七、算法证明
  • 五、算法时间复杂度


前言

贪心算法之应用-单源最短路径-dijkstra算法学习


一、Dijkstra算法简介

解决单源最短路径问题的贪心算法。
【单源最短路径问题】
在一个带权有向网络G=中,每条边e=的权w(e)为非负实数,表示从i到j的距离,网络中有源点s属于V,求从s出发到达每个结点的最短路径。
如图:红色路径为1为源点到各个点最近路线,
解包含两部分:路径路线+最短路径值
贪心算法之应用-单源最短路径-Dijkstra算法学习_第1张图片

二、Dijkstra 算法有关概念

1、x∈S ⇔ x∈V且从s到x的最短路径已经找到
2、初始:S={s},S=V时算法结束
3、从s到i相对于S的最短路径:从s到i且仅经过S中顶点的最短路径。
4、dist[i]:从s到i相对于S最短路径的长度。(若s到i不可达,dist[i]=∞)
5、short[i]:从s到i的最短路径的长度。
6、dist[i]≥short[i]

三、Dijkstra 算法设计思想

输入:有向图G=(V,E,W),V={1,2,3,……,n},s=1
输出:从s到每个顶点的最短路径
1、初始S={1}
2、对于i∈V-S,计算1到i的相对S的最短路,长度dist[i]
3、选择V-S中dist值最小的j,将j加入S,修改V-S中顶点dist的值。

四、Dijkstra伪码

'''
输入:带权有向图G=,源点s属于V
输出:数组L,对所有j属于V-S,L[j]表示s到j的最短路径上j前一个结点的符号
S←{s}
dist{s}←0
for i∈V-S do
   dist[i]←w(s,i)
while V-S≠∅ do
   从V-S中取出具有相对S的最短路径的结点j,k是该路径上连接j的结点
   S←S∪{j}
   L[j]=k
   for i∈V-S do
       if dist[j]+w(j,i)

五、算法运行实例

直接放书本上的上了,写的很完整
贪心算法之应用-单源最短路径-Dijkstra算法学习_第2张图片
贪心算法之应用-单源最短路径-Dijkstra算法学习_第3张图片

六、完整代码

import  numpy as np
def Greedy_Dijkatra(s, n, node_cost):
    S=[]
    S.append(s)
    dist = [np.inf for i in range(n)]
    flag = [0 for i in range(n)]  ##如果flag[i]=1,说明该顶点i已经加入到集合S;否则i属于集合V-S
    L = [np.inf for i in range(n)]
    #print("初始状态的dist与L",np.array(dist),np.array(L))

    for i in range(n):
        dist[i] = node_cost[s][i]  # 初始化源点u到其他各个顶点的最短路径长度
        flag[i] = 0
        if(dist[i] == np.inf):
            L[i] = -1  # 说明源点u到顶点i无边相连,设置L[i]=-1
        else:
            L[i] = s  # 说明源点s到顶点i有边相连,设置L[i]=s
    flag[s] = 1  # 初始化集合S中,只有一个元素:源点s
    dist[s] = 0 # 初始化源点u的最短路径为0,自己到自己的最短路径
    #print("第一步情况下,其他各个结点到源结的相对S的最短路径",dist)
    #print("第一步情况下",L)

    #第i次循环,集合S中有i个结点
    for i in range(1,n+1):
        print("S的情况1",S)
        temp=np.inf
        t=s
        for j in range(n):#在集合V-S中寻找距离源点s最近的顶点t
            #,dist[j]!=0排除自己的情况
            if(flag[j]==0 and dist[j]<temp and dist[j]!=0):
                t=j #记录距离源点u最近的顶点
                temp=dist[j]
        flag[t] = 1  # 找到t,将t加入集合S

        #if t == s : return dist,L #找不到就跳出循环
        for j in range(n):
            #集合S中加入新结点t,更新集合V - S中与t邻接的顶点到s的距离,比较与不包含t时谁的路径值最小,取最小
            if(flag[j]==0  and node_cost[t][j]!=0):
                #flag[j]==0表示j在v-s集合中, node_cost[t-1][j]!=0不是他自己
                if(dist[j] > (dist[t]+node_cost[t][j])):
                         dist[j]=dist[t]+node_cost[t][j]
                         L[j]=t  #记录j的前驱为t
                #print(f"第{i}次下的{j}",dist,L)
        S.append(t)
        #print("S的情况2",S)
        #print(f"第{i}次下的dist,L",dist,L)
    print("dist最后也就是伪代码中的short",dist)
    return dist,L
def step(L,s,i):
    ROOT=[]
    ROOT.append(i)
    #源点和结束点不一样时
    if(s!=i):
        while (L[i] != s):
            ROOT.insert(0, L[i])
            i = L[i]
        ROOT.insert(0, s)
        print("请输出ROOT", ROOT)
    else:
        print("请输出ROOT", ROOT)


if __name__ == '__main__':
    n = int(input("请输入城市个数"))
    node_cost = [[np.inf for i in range(0, n)] for i in range(0, n)]  # 构建全是无穷大的二维列表
    print("当两个城市之间无无路径时用-1表示")
    for i in range(n):
        print(f"城市{i}到各个城市的距离为")
        node_cost[i] = list(map(int, input().split()))
    for i in range(n):
        for j in range(n):
            if(node_cost[i][j]==-1):
                node_cost[i][j]=np.inf
    #print("new node_cost",node_cost)
    s=int(input("请输入源点位置:"))
    dist,L=Greedy_Dijkatra(s,n,node_cost)

    for i in range(n):
        if(dist[i]==np.inf):
            print(f"源点{s}到要去的位置{i},无路可达")
        else:
            print(f"源点{s}到要去的位置{i},最短距离为{dist[i]},路径为")
            step(L, s, i)

贪心算法之应用-单源最短路径-Dijkstra算法学习_第4张图片
贪心算法之应用-单源最短路径-Dijkstra算法学习_第5张图片

七、算法证明

归纳证明思路
命题:当算法进行到第k步时,对于S中每个结点i
       dist[i]=short[i]
归纳基础
  当k=1时,S={s} dist[s]=short[s]=0
归纳步骤
  证明:假设命题对k为真,则对k+1命题也为真
  n=1,S={s} dist[s]=short[s]=0成立
  n=k时,S={s,...i(k)} dist[i]=short[i]成立
  n=k+1时,选择结点v(边为<u,v>,u在s中)
       假设存在另一条从s到v的路径L,路径中第一次离开S的边是<x,y>
       其中x∈S,y∈V-S
       由于算法在这一步选择了结点v并没有选择结点y
       所以一定有①dist[v]≤dist[y]
       且②dist[y]+d(y,v)=L
       d(y,v)表示从y到v的距离
       ①② 得③ dist[v]≤L
       所以算法对v选择的路径是s到v的最短路径,
  故n=k+1时,S={s,...i(k+1)} dist[v]=short[v]成立

贪心算法之应用-单源最短路径-Dijkstra算法学习_第6张图片

五、算法时间复杂度

O(nm)
算法进行n-1步(除去源点,每次往S中加一个结点,n-1个结点)
每步挑选1个具有最小dist函数值的结点进入S,需要O(m)时间

若选用基于堆实现的优先队列的数据结构,可以将算法时间复杂度降到
O(mn)


你可能感兴趣的:(算法,贪心算法,学习)