Project-3:基于堆和循环桶实现 djikstra 算法
-
实验原理
-
堆: 堆是一种经过排序的完全二叉树,其中任一非终端节点的数据值均不大于(或不小于)其左子节点和右子节点的值。最大堆和最小堆是二叉堆的两种形式。最大堆:根结点的键值是所有堆结点键值中最大者。最小堆:根结点的键值是所有堆结点键值中最小者。本次实验使用的是最小堆;
-
桶:桶排序定义:桶排序 (Bucketsort)或所谓的箱排序,是一个排序算法,工作的原理是将数组分到有限数量的桶子里。每个桶子再个别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序)。桶排序是鸽巢排序的一种归纳结果。当要被排序的数组内的数值是均匀分配的时候,桶排序使用线性时间(Θ(n))。但桶排序并不是 比较排序,他不受到 O(n log n) 下限的影响。【来源:百度百科】
可见桶排序的效率非常的高,因此利用桶来寻找最短距离标记有利于提升dijkstra算法的运行效率,不过在真正实现算法的过程中实现的桶与真正的桶排序有一定的区别,这是由dijkstra算法的特点所决定的:即每个桶里面的元素距离标记都一样;但是要保证这一点需要的是在每永久标记一次(即节点出桶一次)桶的头部要往后一个桶移动,并且要保证改变桶的头部之前要把头部桶里面的所有节点清除出桶,然后永久标记;
重复节点如何解决:其实不需要解决,因为第一次永久标记的节点距离标记肯定是最短的,因此在考虑标记某个节点的时候只要判断其是否在永久标记集合当中,如果在,则丢弃不再标记,否者永久标记;或者在把节点加入桶中的时候先判断该节点是否已经永久标记,已经标记就不再加入桶中;
桶的具体实现和封装: 所有的桶结构用一个list()来维持,而一个桶也是一个list(),BucketSet类封装了需要的桶结构,并且对外暴露add(_)和pop_min()方法用于添加元素到桶中和将最小距离标记节点取出,pop_min()方法返回最小距离标记的节点集合;Bucket类是BucketSet类的私有内部类,提供add()和pop_min() 方法功能与BucketSet的两个方法类似,具体更多实现细节见代码注释;
-
-
利用最小堆实现dijkstra算法
(1) 建立最小堆,首先把源节点加入堆中对堆初始化;
(2) 弹出最小距离标记节点;
(3) 将最小距离标记节点永久标记;
(4) 将永久标记节点的所有邻接节点入堆;
(5) 重复2-4操作,直到堆空,算法结束;- 代码实现
from time import time from heapq import heapify, heappop, heappush def heap_dijkstra(graph, s): """ 堆实现迪杰斯特拉算法 :param graph: 图 :param s: 源节点 :return: 各节点距离标记字典 """ node_s = (0, str(s)) # 源节点实体二元组(距离标记,节点) min_heap = list() # 用于建堆的列表 visited = { } # 永久标记节点集合 min_heap.append(node_s) heapify(min_heap) # 初始化最小堆 path_length = { } # 距离标记字典 while min_heap: # 堆非空 min_node = heappop(min_heap) # 取最小距离标记节点 if visited.get(min_node[1]) is None: # 该节点没有永久标记 visited[min_node[1]] = 1 # 永久标记该节点 path_length
- 代码实现