dijkstra算法与优先队列
这是鄙人的第一篇技术博客,作为算法小菜鸟外加轻度写作障碍者,写技术博客也算是对自己的一种挑战和鞭策吧~
言归正传,什么是dijkstra算法呢?
-dijkstra算法是一种解决最短路径问题的简单有效的方法~也算是一种非常naive&effcient的最优化算法吧~
最短路径问题

如上图,从点A->点F,最短路径为A->C->D->F,Min=3+3+3=9
假设用暴力深度搜索遍历所有路径的话,时间复杂度O将会是指数级的(NP-hard Problem)~
dijkstra算法
作为我最喜欢的经典算法之一,dijkstra算法并没有用到高深的图论和拓扑学的知识,
它的核心思想也很简单:贪心法+动态规划
下面贴出维基百科上的伪代码:
1 function Dijkstra(Graph, source):
2 dist[source] := 0 // Distance from source to source
3 for each vertex v in Graph: // Initializations
4 if v ≠ source
5 dist[v] := infinity // Unknown distance function from source to v
6 previous[v] := undefined // Previous node in optimal path from source
7 end if
8 add v to Q // All nodes initially in Q (unvisited nodes)
9 end for
10
11 while Q is not empty: // The main loop
12 u := vertex in Q with min dist[u] // Source node in first case
13 remove u from Q
14
15 for each neighbor v of u: // where v has not yet been removed from Q.
16 alt := dist[u] + length(u, v)
17 if alt < dist[v]: // A shorter path to v has been found
18 dist[v] := alt
19 previous[v] := u
20 end if
21 end for
22 end while
23 return dist[], previous[]
24 end function
贪心的思想体现在 Line12: u := vertex in Q with min dist[u]
从所有的待优化的点Q中找出距离起点最近的点u~
动态规划的思想体现在 Line16-20: 最优表达式为 dist[v] := min{dist[v],dist[u] + length(u, v)}
即如果存在dist[start->u->v]<dist[start->v],则将路径更新~
在dijkstra算法里,每个点的路径可能会被更新N次(length(Q)>N>=0),并不存在一个明确的状态递进。
因此,如何保证在更新N次后,一定能求出最优解,是算法的核心问题~
答案就是贪心法,运用贪心法优化更新的顺序,确保从小到大,更新一遍Q即求出最优解。
优先队列+dijkstra算法
让我们分析一下dijkstra算法的时间复杂度:
总时间复杂度=找最短距离 u := vertex in Q with min dist[u] 的时间复杂度 +
更新距离 dist[v] := min{dist[v],dist[u] + length(u, v)} 的时间复杂度
对于一个无向图G(V,E)来说,
找最短距离的时间复杂度为O(|V|*|V|)(共循环V次,每次V个点),考虑到Q每次递减1,实际复杂度为O(|V|^2/2);
由于图共有E条边,每条边最多被更新2次(1条边2个端点),因此更新距离的时间复杂度为O(2*|E|)。
因此,总时间复杂度=O(2*|E|+|V|^2/2)
然后,实际情况中经常会遇到 |V|^2>>|E| 的稀疏图,即O(2*|E|+|V|^2/2)=O(|V|^2/2)~
因此,如果我们能够优化 findMIN部分,即可大大优化稀疏图下的dijkstra算法~
findMIN的部分优化方法很多,最简单的就是用二分搜索O(logN)代替线性搜索 O(N)~
这里我们将集合Q转化成一个优先队列(priority queue),这样findMIN的时间复杂度变成了O(1),而每次更新priority queue需要花费O(log|V|)~
综上,采用优先队列之后,总时间复杂度=O(2*|E|+|V|*log|V|),
这样的优化对于稀疏图(|V|^2>>|E|)来说,尤为有效~
P.S. 本文仅想谈谈鄙人对dijkstra算法的一些浅见,即粗浅又不够系统,还望各位大牛多多指点~
下一篇博文将会拓展 优先队列(priority queue) 的内容(如果鄙人木有被板砖拍死的话^ ^)
最后贴上鄙人用python实现的dijkstra+priority queue的demo,经测试在G(V=2000,E=10000)时,priority queue能够提升近1倍的运算速度:
# -*- coding: utf-8 -*-
"""
Created on Sun Aug 24 2014 @author: Sperling
"""
import random,time,heapq ###采用优先队列的dijkstra算法###
def dijkstra_pq(conj):
##初始化 各点到起点的最优距离(dis)##
dis=[0]
dis.extend([-1]*(len(conj)-1))
for c in conj[0]:
dis[c[1]]=c[0]
##初始化 待优化的点(排除起点)##
pool=[i for i in range(1,len(conj))]
##初始化 路径记录列表##
pre=[0 if dis[i]>=0 else -1 for i in range(len(conj))]
pre[0]=-1
##初始化 优先队列(Fibonacci heap)##
pq=[(dis[i],i) for i in range(1,len(conj)) if dis[i]>=0]
heapq.heapify(pq) while len(pool)>0:
##找出待优化池中的最短路径点(argmin)##
argmin=-1
while (argmin not in pool) and len(pq)>0:
MIN,argmin=heapq.heappop(pq)
##将最短路径点(argmin)从池中捞出##
if argmin not in pool:
break
else:
pool.remove(argmin)
##更新与argmin直接连通的点<->起点的最短路径##
for c in conj[argmin]:
if c[0]+MIN<dis[c[1]] or dis[c[1]]<0:
dis[c[1]]=c[0]+MIN
pre[c[1]]=argmin
heapq.heappush(pq,(dis[c[1]],c[1])) return dis,pre ###原始dijkstra算法###
def dijkstra(conj):
##初始化 各点到起点的最优距离(dis)##
dis=[0]
dis.extend([-1]*(len(conj)-1))
for c in conj[0]:
dis[c[1]]=c[0]
##初始化 待优化的点(排除起点)##
pool=[i for i in range(1,len(conj))]
##初始化 路径记录列表##
pre=[0 if dis[i]>=0 else -1 for i in range(len(conj))]
pre[0]=-1 while len(pool)>0:
##找出待优化池中的最短路径点(argmin)##
MIN,argmin=1000000,-1
for i in pool:
if dis[i]>0 and MIN>=dis[i]:
MIN,argmin=dis[i],i
##将最短路径点(argmin)从池中捞出##
if argmin<0:
break
else:
pool.remove(argmin)
##更新与argmin直接连通的点<->起点的最短路径##
for c in conj[argmin]:
if c[0]+MIN<dis[c[1]] or dis[c[1]]<0:
dis[c[1]]=c[0]+MIN
pre[c[1]]=argmin return dis,pre ###回溯最优路径###
def trace(pre,p):
route=[]
while p>=0:
route.append(p)
p=pre[p]
return route[::-1] ###构造随机图,不能保证图的连通性###
def randomroute(nV,nE):
##构造nE*nE的随机关联矩阵(mat),其中有2*nV个点是连通的##
mat=[[0 if i==j else -1 for i in range(nV)] for j in range(nV)]
rE=random.sample(xrange(nV*(nV-1)/2),nE)
for k in rE:
i,j=0,k
while i<j:
i+=1
j-=i
i+=1
r=random.randint(1,100)
mat[i][j],mat[j][i]=r,r
##将随机关联矩阵(mat) 转化为 每个顶点的直接连通点列表(route)##
route=[]
for i in range(len(mat)):
route.append([])
for j in range(len(mat[i])):
if mat[i][j]>0:
route[i].append((mat[i][j],j)) return route if __name__=='__main__':
conj=randomroute(2000,10000)
t0=time.clock()
dis,pre=dijkstra(conj)
t1=time.clock()
print 'time_cost dijkstra:%f'%(t1-t0)
print dis[len(conj)-1]
print trace(pre,len(conj)-1) t0=time.clock()
dis,pre=dijkstra_pq(conj)
t1=time.clock()
print 'time_cost dijkstra+priority queue:%f'%(t1-t0)
print dis[len(conj)-1]
print trace(pre,len(conj)-1)
dijkstra算法与优先队列的更多相关文章
- dijkstra算法之优先队列优化
github地址:https://github.com/muzhailong/dijkstra-PriorityQueue 1.题目 分析与解题思路 dijkstra算法是典型的用来解决单源最短路径的 ...
- Dijkstra算法优先队列实现与Bellman_Ford队列实现的理解
/* Dijkstra算法用优先队列来实现,实现了每一条边最多遍历一次. 要知道,我们从队列头部找到的都是到 已经"建好树"的最短距离以及该节点编号, 并由该节点去更新 树根 到其 ...
- HDU 1535 Invitation Cards(逆向思维+邻接表+优先队列的Dijkstra算法)
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1535 Problem Description In the age of television, n ...
- 基于STL优先队列和邻接表的dijkstra算法
首先说下STL优先队列的局限性,那就是只提供入队.出队.取得队首元素的值的功能,而dijkstra算法的堆优化需要能够随机访问队列中某个节点(来更新源点节点的最短距离). 看似可以用vector配合m ...
- Dijkstra算法(朴素实现、优先队列优化)
Dijkstra算法只能求取边的权重为非负的图的最短路径,而Bellman-Ford算法可以求取边的权重为负的图的最短路径(但Bellman-Ford算法在图中存在负环的情况下,最短路径是不存在的(负 ...
- 最短路模板(Dijkstra & Dijkstra算法+堆优化 & bellman_ford & 单源最短路SPFA)
关于几个的区别和联系:http://www.cnblogs.com/zswbky/p/5432353.html d.每组的第一行是三个整数T,S和D,表示有T条路,和草儿家相邻的城市的有S个(草儿家到 ...
- ACM: HDU 1874 畅通工程续-Dijkstra算法
HDU 1874 畅通工程续 Time Limit:1000MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u Desc ...
- 最短路径算法之Dijkstra算法(java实现)
前言 Dijkstra算法是最短路径算法中为人熟知的一种,是单起点全路径算法.该算法被称为是“贪心算法”的成功典范.本文接下来将尝试以最通俗的语言来介绍这个伟大的算法,并赋予java实现代码. 一.知 ...
- [图论]Dijkstra 算法小结
Dijkstra 算法小结 By Wine93 2013.11 1. Dijkstra 算法相关介绍 算法阐述:Dijkstra是解决单源最短路径的算法,它可以在O(n^2)内计算出源点(s)到图中 ...
随机推荐
- (生产)create-keyframe-animation -动画实现
参考:https://github.com/HenrikJoreteg/create-keyframe-animation 实例 var animations = require('create-ke ...
- iOS - 通过view查找所在(viewController)
- (UIViewController *)findViewController:(UIView *)sourceView { id target=sourceView; while (target) ...
- Android中关于XML的一个小问题——使用XML输出“<”错误的问题
在 XML 中,有 5 个预定义的实体引用: < < 小于 > > 大于 & & 和号 ' ' 单引号 " " 引号 注释 ...
- check_mk检测插件编写
参考 Writing Checks (Introduction) Writing agent based checks The New Check API http://www2.steinkogle ...
- java:图片压缩
java使用google开源工具实现图片压缩 :http://www.cnblogs.com/linkstar/p/7412012.html
- oracle-插入到数据库中为日期
oracle中创建一个表,其中一个字段为date,当我们进行插入操作 create table xf_allsalestotal ( xf_txdate date not null, xf_store ...
- Canvas 中drawImage 绘制不出图片
在使用Canvas的drawImage绘制图片时,却发现绘制不出图片,原因是图片是异步加载,图片加载完再绘制. //html <img src="1.png" /> & ...
- C# 安装 Visual Studio IDE
官网: https://visualstudio.microsoft.com/zh-hans/ 下载社区版(免费的) .微软的软件安装都是很nice的.安装过程中选择需要的配置进行安装(比如.net桌 ...
- Vue--父组件传数据给子组件,子组件生命周期过程拿到数据的情况
需求: 在子组件渲染之前,我要修改数据的某个字段 结果是 组件在beforeUpdate,updated 的状态才能拿到父组件的数据 那么证明,我根本无法在beforeUpdate,updated两个 ...
- javascript中parseInt(),08,09,返回0
javascript中在使用parseInt(08).parseInt(09),进行整数转换的时候,返回值是0 工具/原料 浏览器 文本编辑器 方法/步骤 javascript中在使用pa ...