关于dijkstra+heap的实现

来源:互联网 发布:看守所民警知乎 编辑:程序博客网 时间:2024/06/09 12:23

大家说道dijkstra就不得不提它的heap优化。但是具体怎么实现呢?

 

C++ STL提供了priority_queue, 支持push,top,pop操作。但是光靠这三个函数还不足以实现dijkstra的优化。

回想dijkstra算法中,for(1..v)的大循环内,每次在unknown的集合中找到dist[]数组里最小的那个,从unknown集合中删除该结点。朴素实现需要O(V)的时间,而用堆可以用O(log(V))的时间找到。然而,光找到还没完,找到之后要松弛所有与他相邻的unknown的结点(known的结点已经都是最短路了)。注意到如果要用堆实现优化,堆中存储的结点的priority值应当是图中该店当前的离source的距离。然而松弛操作会更新该距离,这就意味着我们要到堆内部找到这个结点的位置,更新距离,再维护堆。而堆是不提供检索功能的,找到一个结点需要O(V),完全糟蹋了O(log V)的优化了。更何况STL的priority_queue没有办法去接触内部的数据了。

 

其实,有一个可行的解决方案:一旦某个结点被更新了距离,一律但当成新结点加进堆里。这样会造成一个一个问题:一个图中的结点可能在堆中可能同时会存在好几个。但是这个不影响结果:先出堆的一定是距离最小的那个结点。其他的结点出堆的时候图里面的对应结点一定已经在known的集合了,到时候直接无视掉弹出来的已经known的结点,继续弹下一个,知道弹出一个unknown的结点为止。

这个做法最坏可能会让堆的高度加倍,然是任然不影响O(log n)的复杂度。不过具体的复杂度计算貌似不那么简单……不过实践证明速度确实有了很大提高,尤其是对于稀疏图。

这种实现用STL的priority_queue也能完成,程序也只是在naive的dijkstra上加几行,性价比不错。

 

而真正的dijkstra+heap也是可以实现的。问题的关键集中在 reduce_to(int ID,int value) 的操作。这要求我们自己实现一个堆。堆的结点需要保存2个数据,一个是对应图中结点的标号ID,一个是对应图中结点的距离dist。而要支持reduce,我们需要一个映射location,使得location[ID]=要找的结点在堆中的位置,这个可以用数组实现。需要注意的是,在维护堆的同时,也需要维护location映射。其实只要在维护堆的代码里稍作修改就能实现了,比想象的要简单。

 

具体代码参考我的前一篇文章

原创粉丝点击