求兩點之間最短路徑-Dijkstra演算法

雲霏霏發表於2015-02-26

 Dijkstra演算法

1.定義概覽

Dijkstra(迪傑斯特拉)演算法是典型的單源最短路徑演算法,用於計算一個節點到其他所有節點的最短路徑。主要特點是以起始點為中心向外層層擴充套件,直到擴充套件到終點為止。Dijkstra演算法是很有代表性的最短路徑演算法,在很多專業課程中都作為基本內容有詳細的介紹,如資料結構,圖論,運籌學等等。注意該演算法要求圖中不存在負權邊。

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

 

2.演算法描述

1)演算法思想:設G=(V,E)是一個帶權有向圖,把圖中頂點集合V分成兩組,第一組為已求出最短路徑的頂點集合(用S表示,初始時S中只有一個源點,以後每求得一條最短路徑 , 就將加入到集合S中,直到全部頂點都加入到S中,演算法就結束了),第二組為其餘未確定最短路徑的頂點集合(用U表示),按最短路徑長度的遞增次序依次把第二組的頂點加入S中。在加入的過程中,總保持從源點v到S中各頂點的最短路徑長度不大於從源點v到U中任何頂點的最短路徑長度。此外,每個頂點對應一個距離,S中的頂點的距離就是從v到此頂點的最短路徑長度,U中的頂點的距離,是從v到此頂點只包括S中的頂點為中間頂點的當前最短路徑長度。

2)演算法步驟:

a.初始時,S只包含源點,即S={v},v的距離為0。U包含除v外的其他頂點,即:U={其餘頂點},若v與U中頂點u有邊,則<u,v>正常有權值,若u不是v的出邊鄰接點,則<u,v>權值為∞。

b.從U中選取一個距離v最小的頂點k,把k,加入S中(該選定的距離就是v到k的最短路徑長度)。

c.以k為新考慮的中間點,修改U中各頂點的距離;若從源點v到頂點u的距離(經過頂點k)比原來距離(不經過頂點k)短,則修改頂點u的距離值,修改後的距離值的頂點k的距離加上邊上的權。

d.重複步驟b和c直到所有頂點都包含在S中。

 執行動畫如下圖(圖片來自網路):

演算法實現如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Dijkstra演算法
{
   class Program
   {
      static int length = 6;
      static string[] shortedPath = new string[length];
      static int noPath = 2000;
      static int MaxSize = 1000;
      static int[,] G = 
      { 
         { noPath, noPath, 10, noPath, 30, 100 }, 
         { noPath, noPath, 5, noPath, noPath, noPath }, 
         { noPath, noPath, noPath, 50, noPath, noPath }, 
         { noPath, noPath, noPath, noPath, noPath, 10 }, 
         { noPath, noPath, noPath, 20, noPath, 60 }, 
         { noPath, noPath, noPath, noPath, noPath, noPath } 
      };
      static string[] PathResult = new string[length];

      static int[] path1 = new int[length];
      static int[,] path2 = new int[length, length];
      static int[] distance2 = new int[length];

      static void Main(string[] args)
      {
         int dist1 = getShortedPath(G, 0, 5, path1);
         Console.WriteLine("Node 0 To 5:");
         for (int i = 0; i < path1.Length; i++)
            Console.Write(path1[i].ToString() + " ");
         Console.WriteLine("Length:" + dist1);

         int[] pathdist = getShortedPath(G, 0, path2);
         Console.WriteLine("\nNode 0 To other:");
         for (int j = 0; j < pathdist.Length; j++)
         {
            Console.WriteLine("Node 0 to " + j + " path:");
            for (int i = 0; i < length; i++)
            {
               Console.Write(path2[j, i].ToString() + " ");
            }
            Console.WriteLine("length:" + pathdist[j]);
         }
         Console.ReadKey();
      }

      //從某一源點出發,找到到某一結點的最短路徑
      static int getShortedPath(int[,] G, int start, int end, int[] path)
      {
         bool[] s = new bool[length]; //表示找到起始結點與當前結點間的最短路徑
         int min;  //最小距離臨時變數
         int curNode = 0; //臨時結點,記錄當前正計算結點
         int[] dist = new int[length];
         int[] prev = new int[length];

         //初始結點資訊
         for (int v = 0; v < length; v++)
         {
            s[v] = false;
            dist[v] = G[start, v];
            if (dist[v] > MaxSize)
               prev[v] = 0;
            else
               prev[v] = start;
         }
         path[0] = end;
         dist[start] = 0;
         s[start] = true;
         //主迴圈
         for (int i = 1; i < length; i++)
         {
            min = MaxSize;
            for (int w = 0; w < length; w++)
            {
               if (!s[w] && dist[w] < min)
               {
                  curNode = w;
                  min = dist[w];
               }
            }

            s[curNode] = true;

            for (int j = 0; j < length; j++)
               if (!s[j] && min + G[curNode, j] < dist[j])
               {
                  dist[j] = min + G[curNode, j];
                  prev[j] = curNode;
               }

         }
         //輸出路徑結點
         int e = end, step = 0;
         while (e != start)
         {
            step++;
            path[step] = prev[e];
            e = prev[e];
         }
         for (int i = step; i > step / 2; i--)
         {
            int temp = path[step - i];
            path[step - i] = path[i];
            path[i] = temp;
         }
         return dist[end];
      }

      //從某一源點出發,找到到所有結點的最短路徑
      static int[] getShortedPath(int[,] G, int start, int[,] path)
      {
         int[] PathID = new int[length];//路徑(用編號表示)
         bool[] s = new bool[length]; //表示找到起始結點與當前結點間的最短路徑
         int min;  //最小距離臨時變數
         int curNode = 0; //臨時結點,記錄當前正計算結點
         int[] dist = new int[length];
         int[] prev = new int[length];
         //初始結點資訊

         for (int v = 0; v < length; v++)
         {
            s[v] = false;
            dist[v] = G[start, v];
            if (dist[v] > MaxSize)
               prev[v] = 0;
            else
               prev[v] = start;
            path[v, 0] = v;
         }

         dist[start] = 0;
         s[start] = true;
         //主迴圈
         for (int i = 1; i < length; i++)
         {
            min = MaxSize;
            for (int w = 0; w < length; w++)
            {
               if (!s[w] && dist[w] < min)
               {
                  curNode = w;
                  min = dist[w];
               }
            }

            s[curNode] = true;

            for (int j = 0; j < length; j++)
            {
               if (!s[j] && min + G[curNode, j] < dist[j])
               {
                  dist[j] = min + G[curNode, j];
                  prev[j] = curNode;
               }
            }
         }
         //輸出路徑結點
         for (int k = 0; k < length; k++)
         {
            int e = k, step = 0;
            while (e != start)
            {
               step++;
               path[k, step] = prev[e];
               e = prev[e];
            }
            for (int i = step; i > step / 2; i--)
            {
               int temp = path[k, step - i];
               path[k, step - i] = path[k, i];
               path[k, i] = temp;
            }
         }
         return dist;

      }
   }
}

 

相關文章