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

    xiaoxiao2022-06-23  22

    一、简介

    单源最短路径-Dijkstra算法,是一种贪心算法,比较好理解,适合初学者。理解概念之后,代码写起来还是很容易的。

    二、定义

    详见:http://wiki.mbalib.com/wiki/Dijkstra算法。感觉解释的很清晰,很有帮助。

    三、题目

    使用网上很标准的一个题目作为例子:

    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 到 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:在代码里打印中文日志,对理解运行还是很有帮助的。

    转载请注明原文地址: https://ju.6miu.com/read-1123150.html

    最新回复(0)