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 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倍的运算速度:

  

  1 # -*- coding: utf-8 -*-
  2 """
  3 Created on Sun Aug 24 2014
  4 
  5 @author: Sperling
  6 """
  7 import random,time,heapq
  8 
  9 ###采用优先队列的dijkstra算法###
 10 def dijkstra_pq(conj):
 11     ##初始化 各点到起点的最优距离(dis)##
 12     dis=[0]
 13     dis.extend([-1]*(len(conj)-1))
 14     for c in conj[0]:
 15         dis[c[1]]=c[0]
 16     ##初始化  待优化的点(排除起点)##  
 17     pool=[i for i in range(1,len(conj))]
 18     ##初始化 路径记录列表##
 19     pre=[0 if dis[i]>=0 else -1 for i in range(len(conj))]
 20     pre[0]=-1
 21     ##初始化 优先队列(Fibonacci heap)##
 22     pq=[(dis[i],i) for i in range(1,len(conj)) if dis[i]>=0]
 23     heapq.heapify(pq)
 24     
 25     while len(pool)>0:
 26         ##找出待优化池中的最短路径点(argmin)##
 27         argmin=-1
 28         while (argmin not in pool) and len(pq)>0:
 29             MIN,argmin=heapq.heappop(pq)
 30         ##将最短路径点(argmin)从池中捞出##
 31         if argmin not in pool:
 32             break
 33         else:
 34             pool.remove(argmin)
 35         ##更新与argmin直接连通的点<->起点的最短路径##
 36         for c in conj[argmin]:
 37             if c[0]+MIN<dis[c[1]] or dis[c[1]]<0:
 38                 dis[c[1]]=c[0]+MIN
 39                 pre[c[1]]=argmin
 40                 heapq.heappush(pq,(dis[c[1]],c[1]))
 41                                
 42     return dis,pre           
 43 
 44 ###原始dijkstra算法###
 45 def dijkstra(conj):
 46    ##初始化 各点到起点的最优距离(dis)##
 47     dis=[0]
 48     dis.extend([-1]*(len(conj)-1))
 49     for c in conj[0]:
 50         dis[c[1]]=c[0]
 51     ##初始化  待优化的点(排除起点)##  
 52     pool=[i for i in range(1,len(conj))]
 53     ##初始化 路径记录列表##
 54     pre=[0 if dis[i]>=0 else -1 for i in range(len(conj))]
 55     pre[0]=-1
 56     
 57     while len(pool)>0:
 58         ##找出待优化池中的最短路径点(argmin)##
 59         MIN,argmin=1000000,-1
 60         for i in pool:
 61             if dis[i]>0 and MIN>=dis[i]:
 62                 MIN,argmin=dis[i],i
 63         ##将最短路径点(argmin)从池中捞出##        
 64         if argmin<0:
 65             break
 66         else:
 67             pool.remove(argmin)
 68         ##更新与argmin直接连通的点<->起点的最短路径##
 69         for c in conj[argmin]:
 70             if c[0]+MIN<dis[c[1]] or dis[c[1]]<0:
 71                 dis[c[1]]=c[0]+MIN
 72                 pre[c[1]]=argmin
 73                            
 74     return dis,pre        
 75 
 76 ###回溯最优路径###
 77 def trace(pre,p):
 78     route=[]    
 79     while p>=0:
 80         route.append(p)
 81         p=pre[p]
 82     return route[::-1]
 83 
 84 ###构造随机图,不能保证图的连通性###
 85 def randomroute(nV,nE):
 86     ##构造nE*nE的随机关联矩阵(mat),其中有2*nV个点是连通的##
 87     mat=[[0 if i==j else -1 for i in range(nV)] for j in range(nV)]
 88     rE=random.sample(xrange(nV*(nV-1)/2),nE)
 89     for k in rE:
 90         i,j=0,k
 91         while i<j:
 92             i+=1
 93             j-=i
 94         i+=1
 95         r=random.randint(1,100)
 96         mat[i][j],mat[j][i]=r,r
 97     ##将随机关联矩阵(mat) 转化为 每个顶点的直接连通点列表(route)##
 98     route=[]
 99     for i in range(len(mat)):
100         route.append([])        
101         for j in range(len(mat[i])):
102             if mat[i][j]>0:
103                 route[i].append((mat[i][j],j))
104         
105     return route
106         
107 if __name__=='__main__':  
108     conj=randomroute(2000,10000)
109     t0=time.clock()
110     dis,pre=dijkstra(conj)
111     t1=time.clock()
112     print 'time_cost dijkstra:%f'%(t1-t0)
113     print dis[len(conj)-1]
114     print trace(pre,len(conj)-1)
115     
116     t0=time.clock()
117     dis,pre=dijkstra_pq(conj)
118     t1=time.clock()
119     print 'time_cost dijkstra+priority queue:%f'%(t1-t0)
120     print dis[len(conj)-1]
121     print trace(pre,len(conj)-1)

 

  

 

转载于:https://www.cnblogs.com/lovedreamf/p/3933911.html

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值