一、简介
单源最短路径-Dijkstra算法,是一种贪心算法,比较好理解,适合初学者。理解概念之后,代码写起来还是很容易的。
二、定义
详见:http://wiki.mbalib.com/wiki/Dijkstra%E7%AE%97%E6%B3%95。感觉解释的很清晰,很有帮助。
三、题目
使用网上很标准的一个题目作为例子:
1、起始点:1 2、目标点:5 3、顶点集合:[1,2,3,4,5,6] 4、路径集合: { '1,2':7, '1,3':9, '1,6':14, '2,3':10, '2,4':15, '3,4':11, '3,6':2, '4,5':6, '5,6':9 } |
|
四、思路
1、所有顶点的集合作为待处理顶点集合 undisposedPoint;
shortestDistanceFinal 记录最终最短路径(从开始点出发);
shortestDistanceNow 记录未确定最短的路径(从开始点出发);
pointDistanceMapNow 记录已获取的距离(从开始点出发) ;
currentPoint 记录当前处理的顶点信息(顶点名称,顶点到开始的的最短路线,顶点到开始点的最小距离)
2、将 startpoint 从 undisposedPoint 中移除
3、从 currentPoint 开始出发,获取到 undisposedPoint 中所有顶点的距离,如果获取不到,则视为无穷大。
4、比较从起点出发经过 currentPoint 到各个顶点的距离,与已记录的距离 pointDistanceMapNow中距离
的大小,更新距离信息(shortestDistanceNow,pointDistanceMapNow )。
5、遍历 shortestDistanceNow 获取当前最小的距离,作为最短路径,获取本次最短路径顶点(这里要注意一
下,不是 currentPoint 到 undisposedPoint 中顶点的最小距离,而是 shortestDistanceNow 中记录的从
开始点开始的所有距离)。更新 shortestDistanceFinal。
6、将 本次最短路径顶点 从 pointList 中移除
7、将 本次最短路径顶点 作为下一个 currentPoint,重复步骤三,直到 最短路径顶点 = endPoint
五、代码实现
Python3.0实现:
## 已知条件
startPoint = 1 # 开始点
endPoint = 5 # 结束点
pointList = [6,5,4,3,2,1] # 顶点集合
pointDistanceMap = { # 已知距离集合
'1,2':7,
'1,3':9,
'1,6':14,
'2,3':10,
'2,4':15,
'3,4':11,
'3,6':2,
'4,5':6,
'5,6':9 }
# 求解过程
# 从距离集合中获取距离信息
# [点一,点二,要查找的距离集合,是否可以反转]
# [线路,是否可达,距离]
def getDistance(p1,p2,distanceMap,canReverse) :
path1 = str(p1) + ',' + str(p2)
distance1 = distanceMap.get(path1,False)
res1 = [path1,True,distance1]
if canReverse :
path2 = str(p2) + ',' + str(p1)
distance2 = distanceMap.get(path2,False)
res2 = [path2,True,distance2]
if distance2 :
if distance1 :
if distance2 < distance1 :
return res2
else :
return res1
else :
return res2
if distance1 :
return res1
else :
return [path1,False,'∞'] # 画个符号吧,好看
undisposedPoint = pointList # 未寻得最小路径的点集合
shortestDistanceFinal = {} # 从开始点出发,最终线路
shortestDistanceNow = {} # 从开始点出发,未确定线路
pointDistanceMapNow = {} # 从开始点出发,所有当前已知距离
currentPoint = (1,'1',0) # 当前点:[当前点,线路,距离]
undisposedPoint.remove(startPoint)
while len(undisposedPoint) != 0 :
print('当初处理顶点信息:',currentPoint,end='\n\n')
for i in undisposedPoint :
currentDistance = getDistance(currentPoint[0],i,pointDistanceMap,True)
print('当前顶点到未处理点距离信息 : ',currentDistance)
# 如果线路可达
if currentDistance[1] :
knownDistance = getDistance(startPoint,i,pointDistanceMapNow,False)
print('历史当前顶点到未处理点距离 : ',knownDistance)
# 如果已知线路为空或者大于新取得的线路,更新已知线路信息
if not knownDistance[1] or \
currentPoint[2] + currentDistance[2] < knownDistance[2] :
shortestDistanceNow[knownDistance[0]] = [ i , currentPoint[1] + ',' + str(i) ,\
currentPoint[2] + currentDistance[2] ]
pointDistanceMapNow[knownDistance[0]] = currentPoint[2] + currentDistance[2]
print('更新后的已知距离 : ',pointDistanceMapNow)
print('更新后的已知线路 : ',shortestDistanceNow,end='\n\n')
# 遍历未确定线路,确定本次最小线路。将最小距离从未确定线路中移除,并添加到最终线路中
shortestPath = ''
shortestDistance = float('inf')
for key in shortestDistanceNow :
if shortestDistanceNow[key][2] < shortestDistance :
shortestPath = key
shortestDistance = shortestDistanceNow[key][2]
currentPoint = [ shortestDistanceNow[shortestPath][0] , \
shortestDistanceNow[shortestPath][1] , \
shortestDistanceNow[shortestPath][2] ]
shortestDistanceFinal[shortestPath] = [ shortestDistanceNow[shortestPath][1], \
shortestDistanceNow[shortestPath][2] ]
# 从未处理顶点中移除本次确定最小距离的点
undisposedPoint.remove(shortestDistanceNow[shortestPath][0])
del shortestDistanceNow[shortestPath]
# del pointDistanceMapNow[shortestPath] # 方便测试,不移除了
print('本次的最小距离:',currentPoint)
print('最终的最小距离:',shortestDistanceFinal)
print('未确定线路信息:',shortestDistanceNow)
print('未处理的顶点集:',undisposedPoint,end='\n\n\n')
if currentPoint[0] == endPoint :
print('从 {0} 到 {1} 最小距离为:{2}'.format(startPoint,endPoint, \
shortestDistanceFinal[str(startPoint)+','+str(endPoint)]))
break
else :
print('目标不可达!')
六、运行结果
打印日志信息如下:
当初处理顶点信息:(1, '1', 0)
当前顶点到未处理点距离信息: ['1,6', True, 14]
历史当前顶点到未处理点距离 : ['1,6',False, '∞']
当前顶点到未处理点距离信息 : ['1,5',False, '∞']
当前顶点到未处理点距离信息 : ['1,4',False, '∞']
当前顶点到未处理点距离信息: ['1,3', True, 9]
历史当前顶点到未处理点距离 : ['1,3',False, '∞']
当前顶点到未处理点距离信息: ['1,2', True, 7]
历史当前顶点到未处理点距离 : ['1,2',False, '∞']
更新后的已知距离: {'1,3': 9, '1,2': 7, '1,6': 14}
更新后的已知线路: {'1,3': [3, '1,3', 9], '1,2': [2,'1,2', 7], '1,6': [6, '1,6', 14]}
本次的最小距离:[2, '1,2', 7]
最终的最小距离:{'1,2': ['1,2', 7]}
未确定线路信息:{'1,3': [3, '1,3', 9], '1,6': [6, '1,6', 14]}
未处理的顶点集:[6, 5, 4, 3]
当初处理顶点信息:[2, '1,2', 7]
当前顶点到未处理点距离信息 : ['2,6',False, '∞']
当前顶点到未处理点距离信息 : ['2,5',False, '∞']
当前顶点到未处理点距离信息: ['2,4', True, 15]
历史当前顶点到未处理点距离 : ['1,4',False, '∞']
当前顶点到未处理点距离信息: ['2,3', True, 10]
历史当前顶点到未处理点距离: ['1,3', True, 9]
更新后的已知距离: {'1,4': 22, '1,3': 9, '1,2': 7, '1,6':14}
更新后的已知线路: {'1,4': [4, '1,2,4', 22], '1,3': [3,'1,3', 9], '1,6': [6, '1,6', 14]}
本次的最小距离:[3, '1,3', 9]
最终的最小距离:{'1,3': ['1,3', 9], '1,2': ['1,2', 7]}
未确定线路信息:{'1,4': [4, '1,2,4', 22], '1,6': [6, '1,6', 14]}
未处理的顶点集:[6, 5, 4]
当初处理顶点信息:[3, '1,3', 9]
当前顶点到未处理点距离信息: ['3,6', True, 2]
历史当前顶点到未处理点距离: ['1,6', True, 14]
当前顶点到未处理点距离信息 : ['3,5',False, '∞']
当前顶点到未处理点距离信息: ['3,4', True, 11]
历史当前顶点到未处理点距离: ['1,4', True, 22]
更新后的已知距离: {'1,4': 20, '1,3': 9, '1,2': 7, '1,6':11}
更新后的已知线路: {'1,4': [4, '1,3,4', 20], '1,6': [6,'1,3,6', 11]}
本次的最小距离:[6, '1,3,6', 11]
最终的最小距离:{'1,3': ['1,3', 9], '1,2': ['1,2', 7], '1,6': ['1,3,6', 11]}
未确定线路信息:{'1,4': [4, '1,3,4', 20]}
未处理的顶点集:[5, 4]
当初处理顶点信息:[6, '1,3,6', 11]
当前顶点到未处理点距离信息: ['5,6', True, 9]
历史当前顶点到未处理点距离 : ['1,5',False, '∞']
当前顶点到未处理点距离信息 : ['6,4',False, '∞']
更新后的已知距离: {'1,4': 20, '1,3': 9, '1,5': 20,'1,2': 7, '1,6': 11}
更新后的已知线路: {'1,4': [4, '1,3,4', 20], '1,5': [5,'1,3,6,5', 20]}
本次的最小距离:[4, '1,3,4', 20]
最终的最小距离:{'1,4': ['1,3,4', 20], '1,3': ['1,3', 9], '1,2': ['1,2', 7], '1,6': ['1,3,6',11]}
未确定线路信息:{'1,5': [5, '1,3,6,5', 20]}
未处理的顶点集:[5]
当初处理顶点信息:[4, '1,3,4', 20]
当前顶点到未处理点距离信息: ['4,5', True, 6]
历史当前顶点到未处理点距离: ['1,5', True, 20]
更新后的已知距离: {'1,4': 20, '1,3': 9, '1,5': 20,'1,2': 7, '1,6': 11}
更新后的已知线路: {'1,5': [5, '1,3,6,5', 20]}
本次的最小距离:[5, '1,3,6,5', 20]
最终的最小距离:{'1,4': ['1,3,4', 20], '1,3': ['1,3', 9], '1,5': ['1,3,6,5', 20], '1,2':['1,2', 7], '1,6': ['1,3,6', 11]}
未确定线路信息:{}
未处理的顶点集:[]
从 1 到5 最小距离为:['1,3,6,5', 20]
七、其他测试数据
1、 startPoint=1 #开始点 endPoint=6 #结束点 pointList=[6,5,4,3,2,1] #顶点集合 pointDistanceMap={ #已知距离集合 '1,2':1, '1,3':1, '2,4':3, '4,6':3, '3,5':1, '5,6':1 }
结果:从 1 到 6 最小距离为:['1,3,5,6', 3] |
|
2、 startPoint=1 #开始点 endPoint=6 #结束点 pointList=[9,8,7,6,5,4,3,2,1] #顶点集合 pointDistanceMap={ #已知距离集合 '1,2':1, '1,7':2, '2,3':2, '2,4':3, '4,5':4, '5,6':1, '6,8':9, '7,8':1, '7,9':1} | |
3、
startPoint=1 #开始点 endPoint=6 #结束点 pointList=[6,5,4,3,2,1] #顶点集合 pointDistanceMap={ #已知距离集合 '1,2':3, '1,4':3, '2,3':1, '2,4':2, '2,6':6, '3,4':1, '3,6':5, '4,5':2, '5,6':4}
结果:从1 到 6 最小距离为:['1,2,6', 9] |
八、完成收工
PS:在代码里打印中文日志,对理解运行还是很有帮助的。