图论算法-最短路径算法

图论算法-最短路径算法

无权最短路径

从图G中,选择s为开始的点,从s到s的最短路径是长为0的路径,将这个信息做个标记;然后开始寻找所有与s距离为1的顶点,将这些顶点做标记;然后开始找出从s出发最短路径恰为2的顶点;直到所有顶点已经被计算。

这种搜索一个图的方法称为***广度优先搜索(breadth-first search)*。该方法按层处理顶点:距开始点最近的那些顶点首先被赋值,而最远的那些顶点最后被赋值。这很像对树的层序遍历(level-order traversal)

伪代码

void Unweighted(Table T)	// Assume T is intialized
{
    int CurrDist;
    Vertex v,w;
    
    for(CurrDist = 0; CurrDist < NumVertex; CurrDist++)
        for each vertex v	// 使用 v 遍历每个点
            if (!T[v].know && T[v].Dist == CurrDist)
            {
                T[v].know = True;	// 表示表 T 中 v 顶点已被访问过
                for each w adjacency to v	// 使用 w 遍历 v 的所有邻接点
                    if(T[w].dist == Infinity)	// 表示顶点 w 的距离是否已被更新过
                    {
                        T[w].dist = CurrDist + 1;
                        T[w].path = v;
                    }
            }
}

由于双层嵌套for循环,因此该算法的运行时间为 O ( ∣ V ∣ 2 ) O(\left | V\right |^2) O(V2)。这个效率明显地低,因为尽管所有的顶点早就被访问过(表 T 中know为True),但是外层循环还是要继续。

使用类似于拓扑排序的做法来排除这种低效性,使用一个队列优化,在迭代开始的时候,队列只含有距离为 C u r r D i s t CurrDist CurrDist的那些顶点。我们添加 C u r r D i s t + 1 CurrDist+1 CurrDist+1的那些邻接顶点时,由于它们自队尾入队,因此这就保证它们直到所有距离为 C u r r D i s t CurrDist CurrDist的那些邻接顶点都被处理之后才被处理。

伪代码

void Unweighted(Table T)
{
    Queue Q;
    Vertex v, w;
    
    Q = CreateQueue(NumVertex);
    MakeEmpty(Q);
    // Enqueue the start vertex S, determined elsewhere
    Enqueue(S, Q);
    while(!IsEmpty(Q))
    {
        v = Dequeue(Q);
        
        for each w adjacency to v
            if(T[w].dist == Infinity)
            {
                T[w].dist = T[v].dist + 1;
                t[w].path = v;
                Enqueue(w, Q);
            }
    }
    DisposeQueue(Q);	// Free the memory
}

Dijkstra算法

解决单源最短路径问题的一般方法叫Dijkstra算法。这个有历史的解法是*贪婪算法(greedy algorithm)*最好的例子。贪婪算法一般地分阶段求解一个问题,在每个阶段它都把当前出现的当作是最好的去处理。

在每个阶段,Dijkstra算法选择一个顶点 v v v,它在所有未知顶点中具有最小的 d v d_v dv,同时算法声明从 s s s v v v的最短路径是已知的。阶段的其余部分有 d w d_w dw值的更新的工作组成。

伪代码

void Dijkstra(Table T)
{
    Vertex v, w;
    
    for(;;)
    {
        v = smallest unknown distance vertex;
        if(v == NotAVertex)
            break;
        T[v].known = True;
        for each w adjacency to v
            if(!T[w].known)
                if(T[v].dist + Cvw < T[w].dist)
                {
                    // Update w
                    Decrease(T[w].dist to T[v].dist + Cvw);
                    T[w].path = v;
                }
    }
}

只要没有边的值为负,该算法总能够顺利完成。如果任何一边出现负值,则算法可能得出错误的答案。总的运行时间为 O ( ∣ V ∣ 2 ) O(\left | V\right |^2) O(V2)

具有负边值的图

如果图出现负边值,那么Dijkstra算法是行不通的。

把带权和不带权的算法结合起来将会解决这个问题。开始,我们把开始的顶点s放入到队列中,然后,在每一阶段我们让一个顶点v出队。找出所有与v邻接的顶点w,使得d_w>d_v+c_{v,w}。然后更新,d_w和p_w,并在w不在队列中的时候把它放到队列中。

伪代码

void WeightedNegative(Table T)
{
    Queue Q;
    Vertex v, w;
    
    Q = CreateQueue(NumVertex);
    MakeEmpty(Q);
   
    while(!IsEmpty(Q))
    {
        v = Dequeue(Q);
        for each w adjacency to v
            if(T[v].dist + Cvw < T[w].dist)
            {
                // Update w
                T[w].dist = T[v].dist + Cvw;
                T[w].path = v;
                if(w is not already in Q)
                    Enqueue(w, Q);
            }
    }
    DisposeQueue(Q);	// Free the memory
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值