单源最短路径-Dijkstra算法 学习笔记

一、简介

单源最短路径-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  }


《单源最短路径-Dijkstra算法 学习笔记》



四、思路

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 到 6 最小距离为:[‘1,3,5,6’, 3]

《单源最短路径-Dijkstra算法 学习笔记》

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}

《单源最短路径-Dijkstra算法 学习笔记》

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]

《单源最短路径-Dijkstra算法 学习笔记》


八、完成收工

PS:在代码里打印中文日志,对理解运行还是很有帮助的。


    原文作者:Dijkstra算法
    原文地址: https://blog.csdn.net/hanhaixingchen/article/details/52535349
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞