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






四、思路

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


并行计算是指同时进行多个计算任务的一种计算方式。单源最短路径算法是用来找出一个图中从一个确定的源点到其他所有点的最短路径的方法。在Python中,可以通过使用并行计算的方法来加速单源最短路径算法的运行。 一种常用的并行计算方法是使用多线程或多进程来同时执行多个计算任务。在Python中,可以使用threading模块或multiprocessing模块来进行多线程或多进程的并行计算。 在并行计算单源最短路径算法中,可以将图的节点分为多个子集,每个子集由一个线程或进程负责计算该子集中节点到源点的最短路径。然后,通过合并各个子集的计算结果来得到整个图的最短路径结果。 在进行并行计算单源最短路径算法时,需要注意以下几点: 1. 确定合适的并行计算策略,如使用多线程还是多进程,根据实际情况选择适合的方法。 2. 合理划分任务,将图的节点划分为多个子集,确保每个子集的计算任务大致相等,以避免负载不均衡问题。 3. 合理管理线程或进程的同步与通信,保证正确地合并各个子集的计算结果。 4. 注意处理并行计算中可能出现的竞争条件和数据一致性问题,使用互斥锁或其他同步机制进行保护。 总之,并行计算可以提高单源最短路径算法的计算效率,减少计算时间,但同时也需要合理的任务划分和管理,保证计算结果的正确性。在Python中,可以使用多线程或多进程的方法进行并行计算。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值