迪杰斯特拉(Dijkstra)算法描述及其正确性证明

1. 算法描述

Dijkstra算法是图论中常用的一个算法,用于计算图中从一个指定点到其余所有点的最短路径。图是有向图,所有边的权重为非负数,图1是满足条件的一个简单有向图。

《迪杰斯特拉(Dijkstra)算法描述及其正确性证明》

图1 有向加权图示例

在图1中,A到D的最短路径是A–>C–>B–>D,其长度为3+2+5=10。

其算法实现代码如下(数据结构与算法分析C++版):

void Dijkstra(Graph *G, int *D, int s){
  int i, v, w;

  for(i = 0; i < G->n(); i++){
    D[i] = INFINITY;
  }
  D[0] = 0;
  for(i = 0; i < G->n(); i++){
    v = minVertex(G, D);
    if(D[v] == INFINITY)
      return;
    G->setMark(v, VISITED);
    for(w = G->first(v); w < G->n(); w = G->next(v, w))
      if(D[w] > (D[v] + G->weight(v,w)))
        D[w] = D[v] + G->weight(v, w);
  }
}

int minVertex(Graph *G, int *D){
  int i, v=-1;

  for(i = 0; i < G->n(); i++){
    if(G->getMark(i) == UNVISITED){
      v = i;
      break;
    }
  }
  for(i++; i < G->n(); i++){
    if((G->getMark(i) == UNVISITED) && (D[i] < D[v]))
    v = i;
  }
  return v;
}

算法中的每个结点使用整数表示,数组D[ ]定义了从出发点s到每个点v的最短路径,被初始化为INFINITY。另外,每个结点还有一个标识(Mark),通过函数setMark和getMark来设置和读取。标识具有两种状态:VISITED和UNVISITED。算法的工作步骤被描述如下:

1. 把所有结点的标识都设为UNVISITED,距离设为INFINIT。

2. 把出发点s的距离设置为0,状态设置为VISITED。

3. 从图中所有UNVISITED的点中选择距离最小的结点v。

4. 设置v的状态为VISITED。

5. 用v来更新其相邻结点。若某相邻结点u的当前距离D[u] > D[v] + weight(v, u),则把u的距离D[u]更新为D[v] + weight(v, u)。

6. 重复3到5步的操作,直到所有结点状态都被设置为VISITED。

以图1为例,算法的计算过程为:

1. D[A] = 0。

2. 选择D[A]为距离最小的结点,设其状态为VISITED;更新D[B] = 10, D[C] = 3, D[D] = 20。

3. 选择D[C]为距离最小的结点,设其状态为VISITED;更新D[B] = D[C] + weight(C, B) = 3 + 2 = 5, D[E] = D[C] + weight(C, E) = 3 + 15 =18。

4. 选择D[B]为距离最小的结点,设其状态为VISITED;更新D[D] = D[B] + weight(B, D) = 5 + 5 = 10。

5. 选择D[D]为距离最小的结点,设其状态为VISITED;与D相连的结点E的距离D[E] = 18,小于D[D] + weight(D, E) = 10 + 11 = 21,所以不需要更新。

6. 选择D[D]为距离最小的结点,设其状态为VISITED;没有相连的点,所以不需要更新。

所以最终每个点的最小距离为D[A] = 0; D[B] = 5]; D[C] = 3]; D[D] = 10; D[E] = 18.

2. 算法正确性证明

上一节描述的Dijkstra算法把图中的结点分为两个部分,分别标记为VISITED和UNVISITED,使用S和V-S来表示。为证明的方便,区分了S和V-S中的点的距离函数,分别为D和D_est,V-S中的距离函数被称为估值函数。算法的主要操作是循环执行第3到5步。证明算法的正确性,可以通过证明每次循环执行之前,S和V-S中的结点满足以下3条属性,执行之后依然满足。

属性1. S中任意m的点的的路径长度D[m]就是其最短路径。

属性2. 估值函数满足下述条件:

《迪杰斯特拉(Dijkstra)算法描述及其正确性证明》

也就是说,对于V-S中的任意结点n,其估值路径D_est[n]是其只通过S中结点的最短路径。

属性3. V-S中估值最小的点n,D_est[n]的值就是其最短路径。

算法第一步S={s},只包含出发结点,且D[s]=0,故而满足属性1,更新相邻结点之后,容易证明,也满足属性2。属性3则需要证明,过程如下。

证明:假设D_est[n]不是n的最短路径,因为D_est是只通过S中结点的最短路径,所以结点n的真实的最短路径必然会经过集合S之外的结点,设路径上第一个非S中的点为j。则真实的最短路径的形式为s->…->j->…->n。因为假设了j之前的点都是S中的,所以根据属性2,D_est[j] < =D[n] < D_est[n],与n是估值最小的结点矛盾,所以属性成立。

算法接下来的操作是把n加入S,并试图更新V-S中结点的距离估值,容易证明,3-5步的一次操作之后,属性1-3仍然满足,所以得证。

    原文作者:Dijkstra算法
    原文地址: https://blog.csdn.net/softee/article/details/39034129
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞