提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
贪心算法之应用-单源最短路径-dijkstra算法学习
解决单源最短路径问题的贪心算法。
【单源最短路径问题】
在一个带权有向网络G=
如图:红色路径为1为源点到各个点最近路线,
解包含两部分:路径路线+最短路径值
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]
输入:有向图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的值。
'''
输入:带权有向图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)
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)
归纳证明思路
命题:当算法进行到第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]成立
O(nm)
算法进行n-1步(除去源点,每次往S中加一个结点,n-1个结点)
每步挑选1个具有最小dist函数值的结点进入S,需要O(m)时间
若选用基于堆实现的优先队列的数据结构,可以将算法时间复杂度降到
O(mn)