Dijkstra算法

這是我在網上搜了好多資料拼接的,不容易啊 (寫的不好請見諒)
Dijkstra算法的要點總結:
1.該算法需要兩個重要的數據結合結構,集合S、T,S集合中存放已經找到最短路徑的節點,T集合中則爲S集合的補集,爲還未找到阻斷路徑的節點集合
2.算法開始時將源點o放入S集合,並初始化各節點到源點的最短路徑,如果之間不相通,距離爲無窮大,否則,當前最短路徑爲到源點o的直接距離
3.整個算法過程爲不斷從T集合尋找當前最短路徑的節點,並將其從T集合轉移到S集合,直到T集合爲空,算法結束
4.每次從T集合中提取當前最短路徑的節點之後需要更新T集合中每個節點的當前最短路徑,更新方法爲:獲取T中節點Ti,依次用S中的每個節點Si,將Si的最短路徑長度加上Si到Ti的距離和與Ti直接到源點的距離做比較,如果距離和更小,則更新Ti的當前最短路徑
5.總流程可表示爲:集合S、T初始化----->提取T中最小路徑節點放入S中------>更新T集合節點的當前最小路徑 ,直到T集合爲空,算法結束


定義

  Dijkstra(迪傑斯特拉)算法是典型的單源最短路徑算法,用於計算一個節點到其他所有節點的最短路徑。主要特點是以起始點爲中心向外層層擴展,直到擴展到終點爲止。Dijkstra算法是很有代表性的最短路徑算法,在很多專業課程中都作爲基本內容有詳細的介紹,如數據結構,圖論,運籌學等等。Dijkstra一般的表述通常有兩種方式,一種用永久和臨時標號方式,一種是用OPEN, CLOSE表的方式,這裏均採用永久和臨時標號的方式。注意該算法要求圖中不存在負權邊。

問題描述

  在無向圖 G=(V,E) 中,假設每條邊 E[i] 的長度爲 w[i],找到由頂點 V0 到其餘各點的最短路徑。(單源最短路徑

迪傑斯特拉算法

  迪傑斯特拉(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算法講解與C/C++實現

  Dijkstra(迪傑斯特拉)算法是典型的最短路徑路由算法,用於計算一個節點到其他所有節點的最短路徑。主要特點是以起始點爲中心向外層層擴展,直到擴展到終點爲止。Dijkstra算法能得出最短路徑的最優解,但由於它遍歷計算的節點很多,所以效率低。
  Dijkstra算法是很有代表性的最短路算法,在很多專業課程中都作爲基本內容有詳細的介紹,如數據結構,圖論,運籌學等等。
  其基本思想是,設置頂點集合S並不斷地作貪心選擇來擴充這個集合。一個頂點屬於集合S當且僅當從源到該頂點的最短路徑長度已知。
  初始時,S中僅含有源。設u是G的某一個頂點,把從源到u且中間只經過S中頂點的路稱爲從源到u的特殊路徑,並用數組dist記錄當前每個頂點所對應的最短特殊路徑長度。Dijkstra算法每次從V-S中取出具有最短特殊路長度的頂點u,將u添加到S中,同時對數組dist作必要的修改。一旦S包含了所有V中頂點,dist就記錄了從源到所有其它頂點之間的最短路徑長度。
  例如,對下圖中的有向圖,應用Dijkstra算法計算從源頂點1到其它頂點間最短路徑的過程列在下表中。
  主題好好理解上圖!
  以下是具體的實現(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
發佈了92 篇原創文章 · 獲贊 11 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章