迪杰斯特拉(Dijkstra)算法

迪杰斯特拉算法
  迪杰斯特拉(Dijkstra)算法思想
  按路径长度递增次序产生最短路径算法:
  把V分成两组:
  (1)S:已求出最短路径的顶点的集合
  (2)V-S=T:尚未确定最短路径的顶点集合,将T中顶点按最短路径递增的次序加入到S中,保证:(1)从源点V0到S中各顶点的最短路径长度都不大于从V0到T中任何顶点的最短路径长度 (2)每个顶点对应一个距离值S中顶点:从V0到此顶点的最短路径长度T中顶点:从V0到此顶点的只包括S中顶点作中间顶点的最短路径长度依据:可以证明V0到T中顶点Vk的最短路径,或是从V0到Vk的直接路径的权值;或是从V0经S中顶点到Vk的路径权值之和(反证法可证)
     求最短路径步骤
  算法步骤如下:
  1. 初使时令 S={V0},T={其余顶点},T中顶点对应的距离值若存在<V0,Vi>,d<v0,Vi)为<V0,Vi>弧上的权值   若不存在<V0,Vi>,d(V0,Vi)为∝
  2. 从T中选取一个其距离值为最小的顶点W且不在S中,加入S
   3. 对T中顶点的距离值进行修改:若加进W作中间顶点,从V0到Vi的距离值比不加W的路径要短,则修改此距离值
  重复上述步骤2、3,直到S中包含所有顶点,即S=T为止
迪杰斯特拉算法的原理
  首先,引进一个辅助向量D,它的每个分量D表示当前所找到的从始点v到每个终点vi的最短路径的长度。如D[3]=2表示从始点v到终点3的路径相对最小长度为2。这里强调相对就是说在算法过程中D的值是在不断逼近最终结果但在过程中不一定就等于最短路径长度。它的初始状态为:若从v到vi有弧,则D为弧上的权值;否则置D为∞。显然,长度为 D[j]=Min{D | vi∈V} 的路径就是从v出发的长度最短的一条最短路径。此路径为(v,vj)。 那么,下一条长度次短的最短路径是哪一条呢?假设该次短路径的终点是vk,则可想而知,这条路径或者是(v,vk),或者是(v,vj,vk)。它的长度或者是从v到vk的弧上的权值,或者是D[j]和从vj到vk的弧上的权值之和。 一般情况下,假设S为已求得最短路径的终点的集合,则可证明:下一条最短路径(设其终点为X)或者是弧(v,x),或者是中间只经过S中的顶点而最后到达顶点X的路径。因此,下一条长度次短的最短路径的长度必是D[j]=Min{D | vi∈V-S} 其中,D或者是弧(v,vi)上的权值,或者是D[k](vk∈S)和弧(vk,vi)上的权值之和。 迪杰斯特拉算法描述如下: 1)arcs表示弧上的权值。若不存在,则置arcs为∞(在本程序中为MAXCOST)。S为已找到从v出发的最短路径的终点的集合,初始状态为空集。那么,从v出发到图上其余各顶点vi可能达到的最短路径长度的初值为D=arcs[Locate Vex(G,v),i] vi∈V 2)选择vj,使得D[j]=Min{D | vi∈V-S} 3)修改从v出发到集合V-S上任一顶点vk可达的最短路径长度。

Dijkstra(迪杰斯特拉)算法是典型的最短路径路由算法,用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。Dijkstra算法能得出最短路径的最优解,但由于它遍历计算的节点很多,所以效率低。
  Dijkstra算法是很有代表性的最短路算法,在很多专业课程中都作为基本内容有详细的介绍,如数据结构,图论,运筹学等等。
  其基本思想是,设置顶点集合S并不断地作贪心选择来扩充这个集合。一个顶点属于集合S当且仅当从源到该顶点的最短路径长度已知。
  初始时,S中仅含有源。设u是G的某一个顶点,把从源到u且中间只经过S中顶点的路称为从源到u的特殊路径,并用数组dist记录当前每个顶点所对应的最短特殊路径长度。Dijkstra算法每次从V-S中取出具有最短特殊路长度的顶点u,将u添加到S中,同时对数组dist作必要的修改。一旦S包含了所有V中顶点,dist就记录了从源到所有其它顶点之间的最短路径长度。

  以下是具体的实现(C/C++):
  /***************************************
  * About: 有向图的Dijkstra算法实现
  * Author: Tanky Woo
  ***************************************/
  #include <iostream>
  using namespace std;
  const int maxnum = 100;
  const int maxint = 999999;
  // 各数组都从下标1开始
  int dist[maxnum]; // 表示当前点到源点的最短路径长度
  int prev[maxnum]; // 记录当前点的前一个结点
  int c[maxnum][maxnum]; // 记录图的两点间路径长度
  int n, line; // 图的结点数和路径数
  // n -- n nodes
  // v -- the source node
  // dist[ ] -- the distance from the ith node to the source node
  // prev[ ] -- the previous node of the ith node
  // c[ ][ ] -- every two nodes' distance
  void Dijkstra(int n, int v, int *dist, int *prev, int c[maxnum][maxnum])
  {
  bool s[maxnum]; // 判断是否已存入该点到S集合中
  for(int i=1; i<=n; ++i)
  {
  dist[i] = c[v][i];
  s[i] = 0; // 初始都未用过该点
  if(dist[i] == maxint)
  prev[i] = 0;
  else
  prev[i] = v;
  }
  dist[v] = 0;
  s[v] = 1;
  // 依次将未放入S集合的结点中,取dist[]最小值的结点,放入结合S中
  // 一旦S包含了所有V中顶点,dist就记录了从源点到所有其他顶点之间的最短路径长度
  // 注意是从第二个节点开始,第一个为源点
  for(int i=2; i<=n; ++i)
  {
  int tmp = maxint;
  int u = v;
  // 找出当前未使用的点j的dist[j]最小值
  for(int j=1; j<=n; ++j)
  if((!s[j]) && dist[j]<tmp)
  {
  u = j; // u保存当前邻接点中距离最小的点的号码
  tmp = dist[j];
  }
  s[u] = 1; // 表示u点已存入S集合中
  // 更新dist
  for(int j=1; j<=n; ++j)
  if((!s[j]) && c[u][j]<maxint)
  {
  int newdist = dist[u] + c[u][j];
  if(newdist < dist[j])
  {
  dist[j] = newdist;
  prev[j] = u;
  }
  }
  }
  }
  // 查找从源点v到终点u的路径,并输出
  void searchPath(int *prev,int v, int u)
  {
  int que[maxnum];
  int tot = 1;
  que[tot] = u;
  tot++;
  int tmp = prev[u];
  while(tmp != v)
  {
  que[tot] = tmp;
  tot++;
  tmp = prev[tmp];
  }
  que[tot] = v;
  for(int i=tot; i>=1; --i)
  if(i != 1)
  cout << que[i] << " -> ";
  else
  cout << que[i] << endl;
  }
  int main()
  {
  freopen("input.txt", "r", stdin);
  // 各数组都从下标1开始
  // 输入结点数
  cin >> n;
  // 输入路径数
  cin >> line;
  int p, q, len; // 输入p, q两点及其路径长度
  // 初始化c[][]为maxint
  for(int i=1; i<=n; ++i)
  for(int j=1; j<=n; ++j)
  c[i][j] = maxint;
  for(int i=1; i<=line; ++i)
  {
  cin >> p >> q >> len;
  if(len < c[p][q]) // 有重边
  {
  c[p][q] = len; // p指向q
  c[q][p] = len; // q指向p,这样表示无向图
  }
  }
  for(int i=1; i<=n; ++i)
  dist[i] = maxint;
  for(int i=1; i<=n; ++i)
  {
  for(int j=1; j<=n; ++j)
  printf("%8d", c[i][j]);
  printf("\n");
  }
  Dijkstra(n, 1, dist, prev, c);
  // 最短路径长度
  cout << "源点到最后一个顶点的最短路径长度: " << dist[n] << endl;
  // 路径
  cout << "源点到最后一个顶点的路径为: ";
  searchPath(prev, 1, n);
  }
  输入数据:
  5
  7
  1 2 10
  1 4 30
  1 5 100
  2 3 50
  3 5 10
  4 3 20
  4 5 60
  输出数据:
  999999 10 999999 30 100
  10 999999 50 999999 999999
  999999 50 999999 20 10
  30 999999 20 999999 60
  100 999999 10 60 999999
  源点到最后一个顶点的最短路径长度: 60
  源点到最后一个顶点的路径为: 1 -> 4 -> 3 -> 5
  最后给出两道题目练手,都是直接套用模版就OK的:
  1.HDOJ 1874 畅通工程续
  2.HDOJ 2544 最短路

你可能感兴趣的:(算法)