1. 程式人生 > >有向圖的最短路徑演算法

有向圖的最短路徑演算法

最短路徑演算法屬於資料結構的圖的應用知識。先介紹基本的圖的概念。

圖由頂點集和邊集組成。(一張圖裡不就是有頂點和邊)。圖中邊帶有方向就是有向圖,否則就是無向圖。圖的儲存結構分為鄰接表和鄰接矩陣。(鄰接表主要採用順序儲存和鏈式儲存結合的方式)。採用連結表這種都是對於稀疏圖而言的(就是邊少對應也就權值少的這種圖叫稀疏圖),而我採用的是鄰接矩陣,因為我的那個圖是個稠密圖,邊的權值很多。採用鄰接矩陣儲存就是兩個方面,一個是用一維陣列儲存頂點資訊,一個是用二位陣列儲存邊的權值資訊。

int[][] graph = {  
			{max,221,294,288,max,400,144,219,264,352,462,484},
			{max,max,175,97,306,289,166,94,228,258,349,364},
			{max,max,max,257,464,462,316,275,405,436,523,538},
			{max,max,max,max,205,203,183,79,184,178,256,269},
			{max,max,max,max,max,83,318,243,225,126,83,79},
			{max,max,max,max,max,max,260,207,152,49,60,84},
			{max,max,max,max,max,max,max,107,124,213,321,343},
			{max,max,max,max,max,max,max,max,130,167,269,285},
			{max,max,max,max,max,max,max,max,max,103,204,230},
			{max,max,max,max,max,max,max,max,max,max,108,132},
			{max,max,max,max,max,max,max,max,max,max,max,26},
			{max,max,max,max,max,max,max,max,max,max,max,max}
		};


實際上對於圖的遍歷分為深度優先搜尋和廣度優先搜尋,而深度優先搜尋主要就是儘可能深的去遍歷,採用遞迴的方式。如果瞭解樹的應用中的回溯法和剪支函式就會對遞迴有很好了解。而我需要對任意一層訪問,所以採用廣度優先搜尋其實和那個樹的層序遍歷很像。層序遍歷需要一個那種暫存的容器記憶正在訪問頂點的下一層節點。這種暫存容器就是先進先出的輔助佇列。而我的佇列都是用一維陣列實現的。

最短路徑演算法思想:

從第一個節點V1開始遍歷,就是遍歷權值矩陣中第一行權值。找出距離V1最短的節點V2。V1到V2的最短距離為D1,然後從V2開始遍歷到其他節點的最短距離,就是遍歷權值矩陣中V2所在的行,找出距離V2最短的點V3,最短權值為D2。然後一直遍歷到最後一個點。並且還設定一個標記位,標記那些已經訪問過的點,就不會被再次訪問。我設定訪問過的點位true,否則為false。但是還需要考慮一種情況:假設V3到V1的D3,此時D3>D1+D2這種情況問題的實質就是當前選擇的直接到的路徑比拐一個點再到終點的路徑還長,不是最短路徑,所以,要加入到整體遍歷的情況中進行判斷。然後就慢慢遍歷吧。

關鍵程式碼:

static int MAX_SIZE=12;

private static int[] stationTemp=new int[MAX_SIZE];//儲存最短路徑經過的點(不包括起始點和終點),如果沒有就都設定為0

private static int[] dist=new int[MAX_SIZE];// 最短路徑長度

private static int[] prev=new int[MAX_SIZE];// 前驅頂點集合;

/**最短路徑演算法

 * @param args

 */

public static void dijkstra(int start,int[][] graph,int[] dist,int[] prev){

       int n=dist.length-1;

       if(start<1||start>n)

             return;

       boolean[] marked=new boolean[n+1];//標記

       for(int i=1;i<=n;i++){

             dist[i]=graph[start][i];

             marked[i]=false;

             if(dist[i]==Integer.MAX_VALUE){

                     prev[i]=0;//將所有節點的前驅都設定為空(除了最後一個節點)

             }else{

                     prev[i]=start;//最後一個節點的前驅為第一個開始遍歷的節點

             }

        }

       dist[start]=0;

       marked[start]=true;//從start點開始遍歷

       for(int i=1;i<n;i++){

             Integer temp=Integer.MAX_VALUE;

             int u=start;

             for(int j=1;j<=n;j++){

                     if((!marked[j])&&(dist[j]<temp)){//遍歷出沒有被遍歷的點並且最短路徑小於最大值

                           u=j;

                           temp=dist[j];//將當前遍歷出來的最短路徑的長度賦值給temp

                     }

              }

/*1.選擇出了從start到下一個點u的最短的路徑。

2.並將start到u的最短路徑長度存入dist[u]中。

*/

             marked[u]=true;//將遍歷後選出的點u作為下一次最短路徑的始發點,將其標誌位為true。

             for(int j=1;j<=n;j++){

                      if((!marked[j])&&(graph[u][j]<Integer.MAX_VALUE)){

                            int newdist=dist[u]+graph[u][j];

                            if(newdist<dist[j]){//如果兩段路徑相加值newdist小於遍歷出來的最短路徑就使用newdist作為當前最短路徑,並將u設定為其前驅。

                                  dist[j]=newdist;

                                  prev[j]=u;

                             }

                       }

              }

       }

}