这是鄙人的第一篇技术博客,作为算法小菜鸟外加轻度写作障碍者,写技术博客也算是对自己的一种挑战和鞭策吧~

  言归正传,什么是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 vsource
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(uv)} 的时间复杂度

    对于一个无向图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算法与优先队列的更多相关文章

  1. dijkstra算法之优先队列优化

    github地址:https://github.com/muzhailong/dijkstra-PriorityQueue 1.题目 分析与解题思路 dijkstra算法是典型的用来解决单源最短路径的 ...

  2. Dijkstra算法优先队列实现与Bellman_Ford队列实现的理解

    /* Dijkstra算法用优先队列来实现,实现了每一条边最多遍历一次. 要知道,我们从队列头部找到的都是到 已经"建好树"的最短距离以及该节点编号, 并由该节点去更新 树根 到其 ...

  3. HDU 1535 Invitation Cards(逆向思维+邻接表+优先队列的Dijkstra算法)

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1535 Problem Description In the age of television, n ...

  4. 基于STL优先队列和邻接表的dijkstra算法

    首先说下STL优先队列的局限性,那就是只提供入队.出队.取得队首元素的值的功能,而dijkstra算法的堆优化需要能够随机访问队列中某个节点(来更新源点节点的最短距离). 看似可以用vector配合m ...

  5. Dijkstra算法(朴素实现、优先队列优化)

    Dijkstra算法只能求取边的权重为非负的图的最短路径,而Bellman-Ford算法可以求取边的权重为负的图的最短路径(但Bellman-Ford算法在图中存在负环的情况下,最短路径是不存在的(负 ...

  6. 最短路模板(Dijkstra & Dijkstra算法+堆优化 & bellman_ford & 单源最短路SPFA)

    关于几个的区别和联系:http://www.cnblogs.com/zswbky/p/5432353.html d.每组的第一行是三个整数T,S和D,表示有T条路,和草儿家相邻的城市的有S个(草儿家到 ...

  7. ACM: HDU 1874 畅通工程续-Dijkstra算法

    HDU 1874 畅通工程续 Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Desc ...

  8. 最短路径算法之Dijkstra算法(java实现)

    前言 Dijkstra算法是最短路径算法中为人熟知的一种,是单起点全路径算法.该算法被称为是“贪心算法”的成功典范.本文接下来将尝试以最通俗的语言来介绍这个伟大的算法,并赋予java实现代码. 一.知 ...

  9. [图论]Dijkstra 算法小结

    Dijkstra 算法小结  By Wine93 2013.11 1. Dijkstra 算法相关介绍 算法阐述:Dijkstra是解决单源最短路径的算法,它可以在O(n^2)内计算出源点(s)到图中 ...

随机推荐

  1. spring笔记3-AOP

    一.概述 AOP:(Aspect Oriented Programming)即:面向切面编程.把我们程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在不修改源码的基础上,对我们的已有方法 ...

  2. C++ Knowledge series 3

    Programming language evolves always along with Compiler's evolvement The Semantics of Data The size ...

  3. AIR Native Extension for iOS 接入第三方sdk 如何实现 AppDelegate 生命周期

    作者:Panda Fang 出处:http://www.cnblogs.com/lonkiss/p/6492385.html 原创文章,转载请注明作者和出处,未经允许不可用于商业营利活动 去年到今年做 ...

  4. 如何通过C#实现网页信息采集的方法总结

    Internet上有着极其庞大的资源信息,各行各业的信息无所不有.网页的信息搜集就是获取网页的数据,然后通过程序分析,将有用的数据提取分离出来.搜索引擎工作的一部分就是网页数据抽取.比如编制程序抽取新 ...

  5. mysql :SQL语句中的替换函数replace

    replace() 用第三个表达式替换第一个字符串表达式中出现的所有第二个给定字符串表达式. 语法 REPLACE ( 'string_expression1' , 'string_expressio ...

  6. April 7 2017 Week 14 Friday

    A good heart is better than all the brains in the world. 聪明绝顶,不如宅心仁厚. A good heart can be useful to ...

  7. 2018.8.25 JVM

    一.JVM内存区域 Java虚拟机在运行时,会把内存空间分为若干个区域,根据<Java虚拟机规范(Java SE 7 版)>的规定,Java虚拟机所管理的内存区域分为如下部分: 方法区 堆 ...

  8. python实现连续子数组的最大和

    题目描述 HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学.今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决.但是,如果向量 ...

  9. bind改变this的指向

    <script type="text/javascript"> var Hello = function(){ this.setT = function(){ wind ...

  10. mysql随机字符串函数

    drop function if exists rand_str; delimiter $$ ) charset 'utf8' begin # 定义接收初始化类型 ) ; # 定义初始化数字 ) '; ...