本文转载本人独立博客:http://zhexue.sinaapp.com/?p=13
最短路径问题,一个经典算法问题。本文粗略总结了一种常见的最短路径算法,以及几个最短路径变种问题的解法,其中包括哈密顿路。对于有向图或者无向图,假设有V个节点,E条边,G[Vi,Vj]表示图中点Vi到Vj边的权值。dist[i]表示:点s到点i的最短路径。
一、单源最短路径
给定图G,求点对s->t之间的最短路径,该问题使用经典的dijkstra算法即可解决,时间复杂度O(V^2)。基本思想:两个集合S,T,S表示已经访问的点集合,T表示未访问的点集合,S初始为空,T包括所有点;每次从T集合中选取从s到该点距离最小的点cur,然后将点cur加入到S中(保证从s到S集合中的点之间的路径长度最小),并且基于cur点为跳板,做松弛操作,更新s到T集合中其他点的距离,松弛操作即,如果dist[j] > dist[cur] + G[cur,j],更新dist[j] = dist[cur]+G[cur,j],其中j属于T集合;当cur==t时算法结束。
dijkstra代码下载
二、有负权边的图的单源最短路径
对于(一)中的dijkstra算法,是否可以用于求解带负权边的单源最短路径问题呢?用三元组(x,y,w)表示一条边权为w的从点x到点y的有向边。先举例看看,假设图中包含3个节点,包含3条边:(1,2,-3)、(2,3,1)、(3,1,1),从图可以看出为一个环1->2->3->1,且环的边权总权值为-3+1+1=-1,那么通过一直循环,那么图中任意两点之间的最短路径都为-oo大,因此不能通过dijkstra来求解最短路径,因为出现负环之后破坏了“从s到集合S中点之间路径长度最小”这点,通过负环的循环,s到S中点之间的路径长度还可以变小。
对付有负权边的单源最短路径问题,可以采用bellman-ford算法、SPFA算法。
Bellman-ford算法思想:dist[s] = 0,其他点i ,dist[i]=oo。进行V-1次循环,每一次循环:对图每一条边E(i,j)两边的点做松弛操作,如果dist[j] > dist[i] + G[i,j],更新dist[j] = dist[i]+G[i,j]。完成V-1次循环后,进行判断:如果存在一条边E(i,j),如果dist[i]+G[i,j] < dist[j],那么图中存在负权环。如果不存在负权环,则dist[t]为从s到t的最短路径。算法复杂度O(VE)。
Bellman-ford算法代码下载
SPFA算法思想:维护一个队列Q,队列初始只有s点,一个标记数组flag,flag[i]=1表示节点i在队列中,否则表示不在队列中,一个cnt数组,cnt[i]标记点i进入队列的次数。求队首元素cur,对于边E(cur,j),进行松弛操作:如果dist[j] > dist[cur] + G[cur,j],更新dist[j] = dist[cur]+G[cur,j],如果j不在队列中,则将j加入队尾,同时判断j进入队列次数是否大于V-1,如果大于V-1,说明存在负权环,算法结束,否则一直进行,直到队列为空为止。算法复杂度O(2E)。
SPFA算法代码以及论文下载
三、大规模的图,顶点多的稀疏图
Dijkstra算法复杂度为O(V^2),如果图的规模太大,那么无疑难以胜任。其实,对与规模大的图,可以使用min-heap优化,复杂度O((V+E)logV)。思想:维护一个最小堆,用于优化Dijkstrak中从T选取从s到T中路径最短的点,该点即堆顶元素。这个方法即A*搜索。
Dijkstra+heap代码下载
四、全源最短路径问题
全源最短路径即求出图中任意点对之间的最短路径。方法(1):枚举任意点对,采用dijkstra算法求解即可,复杂度O(V^4)。方法(2):以每一个点为松弛操作的中间点,枚举其他两点,进行松弛操作,即可得到全源最短路径,这便是鼎鼎大名的floyd算法,其状态转移方程如下: G[i,j]=min{G[i,k]+G[k,j],G[i,j]},时间复杂度O(V^3)。
floyd算法代码下载
五、最短哈密顿路径
从s出发到达t,且经过图中每个点至少一次的最短路径长度。这个问题是一个NPC问题,没有高效的解法。假设有N个点,那么N位bit来标记那些点已经访问过,哪些没有访问过。设f[I][J]表示,从s出发达到J,且经过了I中对应位标记为1的所有点的最短路径。有方程如下:
f[I1][J1] = min{F[I][J] + G[J][j], 枚举I,J,j,其中(I&(1<<j)) == 0 && (I|(1<<j) )== I1 && (I&(1<<J)) != 0}
初始只f[(1<<s)][s] = 0, 从改点出发,利用上述方程推出所有的中间变量,包括结果f[(1<<V)-1][t]。下面代码用于求解小规模图的哈密顿路。
代码下载
六、第K短路径问题
求s到t的第k短路径,如果k=1,直接采用dijkstra算法即可求解。如果k=2的话,首先采用dijkstra算法求解最短路径,然后枚举删除最短路径上边,再次进行dijkstra算法,求解最短路径即为第k短路径。
理论一:A*算法求解到的路径是最短的。
根据理论一就可以用A*路径求得最短路径,比dijkstra盲目式算法效率高。
假设用A*算法求得最短路径时,即第一次搜索到目标节点后不停止。继续启发式搜索下去,那么根据理论一可以得到第二次搜索到目标节点的路径是第二短路径。依次类推得到第k短路径。
那么A*算法的h’(x)怎么设计呢?
已知h’(x)与h(x)越接近,时间效率越好,h(x)为x到目标节点的实际最短路长。既然这样那么直接取最好值,先用dijkstra算法算出各点到目标节点的最短路径作为估价值h’(x),使效率到达极大。
第K短路径代码下载