Dijkstra算法的两种实现

Dijkstra算法是用来求加权图的单源最短路径算法之一,其实现方法有两种利用动态规划和贪婪算法。以下内容中,V代表节点个数,E代表边数,初始结点为start,w(i, j)代表边i->j的权重。

1、动态规划算法

先定义一个数组dp,dp[i]用来记录从start结点到i结点的最短路径,初始值为∞。然后执行以下循环直到dp中没有∞出现,对于每次循环,遍历每条边v1->v2,更新dp数组为dp[v2] = min(dp[v2], dp[v1] + w(v1, v2)),伪代码如下:

定义dp;
while(dp中有∞):
    //备份dp
    tmp = dp;
    for(图中的每条边v1->v2):
        dp[v2] = min(dp[v2], tmp[v1] + w(v1, v2));

return dp[end];

然而,这个算法有一些冗余操作:可以看出内部循环体每次都要遍历图中的所有边 。第i次外层循环,min函数更新的结点其实只有第i-1次外层循环中数值发生变化的结点的邻接节点,因为对于第i次循环中的结点v2,若其父结点均未在i-1次循环中更新,那么dp[v2]的值不会在i次循环中有变化,这就导致很多次循环空跑。

因此可以设置一个队列,每次将更新的结点入队,while循环的条件也可用队列是否为空来判断,即若上次循环没有更新的结点,那么所有结点均找到了最短路径,伪代码如下:

定义dp
定义queue
queue.push(start);
while(!queue.empty()):
    pre = queue.pop();
    tmp = dp;
    for(每一条以tmp为起点的边pre->v):
        if(dp[v] > tmp[pre] + w(pre, v)):
            dp[v] = tmp[pre] + w(pre, v);
            queue.push(v);

return dp[end];

2、贪婪算法

贪婪算法关注的是每次都选择最合适的,具体在最短路径中是每次都选择路径最短的结点。将整个结点集分为S和V-S,S中的结点已经找到从start开始到此结点的最短路径,V-S则是未找到从start开始的最短路径。对于每次循环,在V-S中寻找一个与S中结点路径最短的结点v加入集合S,即从start到v没有更短的路径,并更新v的邻接节点。用一个flag数组记录节点v是否在S中,若在,则flag[v]为true,反之为false。伪代码如下:

for(每个结点v):
    distance[v] = ∞;
    flag[v] = false;

distance[start] = 0;
flag[start] = true;
for(每个结点):
    v = flag为false中的distance最小的结点;
    flag[v] = true;
    for(v的每个邻接节点vn):
        if(flag[vn] == false && distance[vn] > distance[v] + w(v, vn)):
            distance[vn] = distance[v] + w(v, vn);
    

若要记录路径具体组成,可用prev数组记录每个结点的父节点即可。 

 

你可能感兴趣的:(算法与数据结构)