目录
一.dijstra算法重述
1.基本思想:
基本思想:
1.从顶点s开始逐步扩展边界;
2.每次扩展,选择当前边界边中 满足贪心准则的边e(u*,v*); 确定s到v*的最短路径。 【加入点的被永久标记】
3.直至所有顶点均被永久标记。
2.基本的伪代码
X = {s}; //永久标记顶点集合
A[s]=0; //距离标记
B[s]=NULL; //路径
WHILE X 不等于 V DO
1) 选择边界边中A[u]+w(u,v)最小的边e(u*,v*);
2) X = X U {v*};
3) A[v*]=A[u*]+w(u*,v*);
4) B[v*]=B[u*] U e(u*,v*);
ENDWHILE
3.正确性证明
证明要求:
任给边权非负的有向加权图,Dijkstra算法都能正确计算所有的最短路距离。即: A[v]=D[v], ∀v∈V
其中A[v]代表:算法计算的距离
D[v]代表:真实的最短路的距离
下面是一个证明的连接:https://www.cs.auckland.ac.nz/software/AlgAnim/dij-proof.html
当然也可以跟随作者和作者老师的思路进行证明:
证明思路:对循环过程进行归纳证明。
基础步骤:A[s]=D[s]=0 成立。
归纳假设:以前的循环中,算法都是正确的。
即:∀v∈X, A[v]=D[v],且B[v]记录了真实最短路
归纳步骤:在归纳假设下证明当前循环结束时结果正确。
注:上述图片来源于电子科技大学网路算法基础挑战性课程PPT
注:上述图片来源于电子科技大学网路算法基础挑战性课程PPT
4.dijstra的不同实现方式
4.1最直接的实现方式
注:上述图片来源于电子科技大学网路算法基础挑战性课程PPT
4.2数组实现方式
1.用一个数据结构来维护所有 的边界点;
2.相当于所有边界边被分成不同 的小集合;
3.与同一个边界点相关联的那些 边只记录一个最小值。
伪代码:
FOR all vertex j in V DO
A[j] = ; p[j] = NULL;
X = {s}; A[s]=0; p[s] = NULL;
WHILE X V DO
i = FindMin(V-X);
X = X U {i};
FOR every neighbor t of i
IF A[i] + w(i, t) < A[t]
A[t] = A[i] + w(i, t);
p[t] = i;
ENDFOR
ENDWHILE
4.3堆实现方式
与实现4.2类似,只是用堆来维护V-X集合,key为A值。
数据结构堆的介绍:
https://www.jianshu.com/p/6b526aa481b1
伪代码:
FOR all vertex j in V DO
A[j] = ; p[j] = NULL;
X = {s}; A[s]=0; p[s] = NULL;
WHILE X V DO
i = FindMin(V-X);
X = X U {i};
FOR every neighbor t of i
IF A[i] + w(i, t) < A[t]
A[t] = A[i] + w(i, t);
p[t] = i;
ENDFOR
ENDWHILE
4.4循环桶实现方式
与实现4.2类似,但用桶来维护V-X集合。
数据结构桶:
https://www.jianshu.com/p/be7643cc073a
伪代码:
FOR all vertex j in V DO
A[j] = ; p[j] = NULL;
X = {s}; A[s]=0; p[s] = NULL;
WHILE X V DO
i = FindMin(V-X);
X = X U {i};
FOR every neighbor t of i
IF A[i] + w(i, t) < A[t]
A[t] = A[i] + w(i, t);
p[t] = i;
ENDFOR
ENDWHILE
二.python实现dijstra
1.基于数据结构堆实现dijstra
from collections import defaultdict
data_edge=defaultdict(list)
nodes=[] #储存点
def build_graph(filepath,data_edge,nodes):#构造图
f=open(filepath,'r')
edge_num=int(f.readline())
f.readline()
for i in range(0,edge_num):
node_a,node_b,edge_c=f.readline().split()
data_edge[node_a].append((int(edge_c),node_a,node_b))
if node_a not in nodes:
nodes.append(node_a)
if node_b not in nodes:
nodes.append(node_b)
start_node=f.readline().strip()#获取源点
dis_node=f.readline().strip()#获取宿点
f.close()
return str(start_node),str(dis_node)
class heap(object):
"""堆"""
def __init__(self):
self.data=[]
def get_parent_index(self,index):#获取父节点的下标
if index==0 or index>len(self.data)-1:
return None
else:
return (index-1)>>1
def Exact_min(self):#取出最小值
remove_data = self.data[0]
self.data[0] = self.data[-1]
del self.data[-1]
self.heapify(self.data)# 将剩余数据堆化
return remove_data
def swap(self,index_a,index_b):#交换结点数据
self.data[index_a], self.data[index_b]=self.data[index_b], self.data[index_a]
def insert(self,data_key):#插入新来结点数据
self.data.append(data_key)#先加到最后
index=len(self.data)-1
parent=self.get_parent_index(index)#先加入到堆的后面
while parent is not None and self.data[parent][0]>=self.data[index][0]:# 交换操作
self.swap(parent, index)
index=parent
parent=self.get_parent_index(parent)
def heapify(self, arr):
self.data=arr
#print(arr)
#print(self.data)
index=self.get_parent_index(len(arr)-1)
#print(index)
while index!=None and index>=0 :
if(2*index+1)<len(self.data):
if self.data[(2*index+1)]<self.data[index]:
self.swap(2*index+1,index)
if (2*index+2)<len(self.data) and (self.data[(2*index+2)]<self.data[index]):
self.swap(2*index+2,index)
index=index-1
heap1=heap()
def dijkstra(data_edge,start_node,end_node,heap1):#
distance=[]#记录已探索边界点和边界点最短距离
path={}#保存与源点之间最短路路径数值
path[start_node]=0#起始点的距离值初始化为0
path_list=defaultdict(list)#存储每个点的路径
visited=[]#记录一探索的节点的集合
visited.append(start_node)
path_list[start_node].append(start_node)
for i in data_edge[start_node]:
edge_c,node_a,node_b=i
distance.append((edge_c,node_a,node_b))
# path_list[node_b].append(node_a)
heap1.heapify(distance)
while end_node not in visited:
present_min_distance,front_node,node=heap1.Exact_min()#选出距离最小的边数据
for i in path_list[front_node]:#把它前面结点的路径加入新节点的路径中
path_list[node].append(i)
path_list[node].append(node)
if node not in visited:
visited.append(node)
path[node]=present_min_distance
for neighbor in data_edge[node]:#更新边集合
edge_c,node,neighbor_node=neighbor
present_min_distance=path[node]+edge_c
if neighbor_node not in visited:
heap1.insert((present_min_distance,node,neighbor_node))#更新边界点
return path,path_list#返回每个点距离源点源点最短路径的数值,和路径
a,b=build_graph('project5-test3',data_edge,nodes)
ans,ans_list=dijkstra(data_edge,a,b,heap1)
print("权重和:%d"%ans[b])#输出最短路径距离源点的最短路径
print('路径:')
print(ans_list[b])
2.基于数据结构-循环桶实现dijstra
from collections import defaultdict
class bucket(object):
"""循环桶"""
def __init__(self,ration):
self.ration=int(ration)#桶的容量
# print(self.ration)
self.data=[[] for i in range(0,int(ration))]
self.front=1#桶前指针
def insert(self,newdata):
index=(newdata[0])%self.ration
self.data[index].append(newdata)
def Exact_min(self): #取出离桶前指针最近的桶中第一个元素
while not self.data[(self.front)%self.ration]:
self.front=self.front+1
key=self.data[self.front % self.ration][0]
self.data[self.front % self.ration].pop(0)
self.front=self.front+1
return key
nodes=[]
edges=defaultdict(list)
edge_size=[]
def read_file(filepath,nodes,edges,edge_size):#读取文件数据
f=open(filepath,'r')
edge_num=int(f.readline())
node_num=int(f.readline())
for i in range(0,edge_num):
node_a,node_b,edge_c=f.readline().split()
edges[node_a].append((int(edge_c),node_a,node_b))
edge_size.append(int(edge_c))
if node_a not in nodes:
nodes.append(node_a)
if node_b not in nodes:
nodes.append(node_b)
start=f.readline().strip()
end=f.readline().strip()
f.close()
return edge_num,node_num,start,end
def find_edge_max(edge_size):#找出最大边
return max(edge_size)
def dijkstra(edges,start_node,bucket1,):
path={}#存储每个点离源点的最短路径
path[start_node]=0
path_list = defaultdict(list) # 存储每个点的路径
visited=[]#已经永久标记过的点
visited.append(start_node)
path_list[start_node].append(start_node)
for i in edges[start_node]:
edge_c,start_node,node_next=i
bucket1.insert((edge_c,start_node,node_next))
while len(visited)!=len(nodes):
min_current_distance,front_node,node=bucket1.Exact_min()
for i in path_list[front_node]:#把它前面结点的路径加入新节点的路径中
path_list[node].append(i)
path_list[node].append(node)
if node not in visited:
path[node]=min_current_distance
visited.append(node)
for neighbor in edges[node]:
if neighbor[2] not in visited:
edge,node_a,node_b=neighbor
current_distance=edge+path[node_a]
bucket1.insert((current_distance,node_a,node_b))
return path,path_list #返回各点最短权重和路径
a,b,c,d=read_file('project5-test3',nodes,edges,edge_size)
maxedge=find_edge_max(edge_size)
bucket1=bucket(find_edge_max(edge_size)+1)
ans,ans_list=dijkstra(edges,c,bucket1)
print("权重和:")
print(ans[d])
print("路径:")
print(ans_list[d])