1. 程式人生 > >有權圖單源最短路徑——ijkstra演算法

有權圖單源最短路徑——ijkstra演算法

  從一個源點到其他各頂點的最短路徑問題稱為“單源最短路徑問題”。
  • 最短路徑的最優子結構性質

    • 該性質描述為:如果P(i,j)={Vi…Vk…Vs…Vj}是從頂點i到j的最短路徑,k和s是這條路徑上的一箇中間頂點,那麼P(k,s)必定是從k到s的最短路徑。下面證明該性質的正確性。
    • 假設P(i,j)={Vi…Vk…Vs…Vj}是從頂點i到j的最短路徑,則有P(i,j)=P(i,k)+P(k,s)+P(s,j)。而P(k,s)不是從k到s的最短距離,那麼必定存在另一條從k到s的最短路徑P’(k,s),那麼P’(i,j)=P(i,k)+P’(k,s)+P(s,j)<P(i,j)。則與P(i,j)是從i到j的最短路徑相矛盾。因此該性質得證。
  • Dijkstra演算法

    • 定義dist[W] = S到W的最短距離; dist[S] = 0; path[W] = S到W路徑上經過的頂點(即W前一個父頂點)。
    • 由上述性質可知,如果存在一條從i到j的最短路徑(Vi…Vk,Vj),Vk是Vj前面的一頂點。那麼(Vi…Vk)也必定是從i到k的最短路徑。為了求出最短路徑,Dijkstra就提出了以最短路徑長度遞增,逐次生成最短路徑的演算法。 譬如對於源頂點s,
      • 選擇其直接相鄰的頂點中長度最短的頂點V,此時將V收錄到集合S ={源點s + 已經確定了最短路徑的頂點V}中;
      • V進入集合S後,將有可能影響V一圈鄰接點W的dist值,dist[W] = min{dist[W], dist[V] + <V, W>的權重};
      • 最短路徑找到,停止。
  • 演算法複雜度

    • FinMinDist可以用小頂堆實現。 在這裡插入圖片描述
  • 參考程式碼

/* 
	Name: Dijkstra演算法 
	Copyright: 
	Author: xuuyann 
	Date: 07/11/18 14:31
	Description: 
*/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <stack>
using namespace std;

//圖的鄰接矩陣表示法
#define MaxVertexNum 100 
//最大頂點數設為100 #define INFINITY 65535 //無窮設為雙位元組無符號整數的最大值65535 typedef int Vertex;//用頂點下標表示頂點,為整型 typedef int WeightType;//邊的權值設為整型 typedef char DataType;//頂點儲存的資料型別設為字元型 //圖結點的定義 typedef struct GNode *PtrToGNode; struct GNode { int Nv;//頂點數 int Ne;//邊數 WeightType G[MaxVertexNum][MaxVertexNum];//鄰接矩陣,表示頂點間的相鄰關係 DataType Data[MaxVertexNum];//存頂點的資料 //注意:如果頂點無資料,此時Data[]可以不用出現 }; typedef PtrToGNode MGraph;//用鄰接矩陣儲存圖的型別 //邊的定義 typedef struct ENode *PtrToENode; struct ENode { int V1, V2;//邊的兩個頂點 WeightType Weight;//邊的權重 }; typedef PtrToENode Edge; MGraph CreateGraph(int VertexNum) //圖的建立,並初始化一個具有VertexNum個頂點且Edge為0的空圖 { MGraph Graph = (MGraph)malloc(sizeof(struct GNode )); Graph->Nv = VertexNum; Graph->Ne = 0; for (int i = 0; i < Graph->Nv ; i++){ for (int j = 0; j < Graph->Nv ; j++ ) Graph->G[i][j] = INFINITY; } return Graph; } void InsertEdge(MGraph Graph, Edge E) //插入邊 { Graph->G[E->V1 ][E->V2 ] = E->Weight ; //若是無向圖,則還要插入邊<V2, V1> //Graph->G[E->V2 ][E->V1 ] = E->Weight ; } Vertex FindMinDist(MGraph Graph, int *dist, int *collected) //返回未被收錄頂點中dist最小者 { Vertex MinV, V; int MinDist = INFINITY; for (V = 0; V < Graph->Nv ; V++){ if (collected[V] == false && dist[V] < MinDist){//如果V未被收錄並且dist[V]更小 MinDist = dist[V];//更新最小距離 MinV = V;//更新對應頂點 } } if (MinDist < INFINITY)//若找到最小dist return MinV; else return -1; } bool Dijkstra(MGraph Graph, int *dist, int *path, Vertex S) //dist[V]=S到V的最短距離,dist[S]=0,path[V]=S到V路上經過的父頂點 { int collected[MaxVertexNum]; Vertex V, W; //初始化:此處預設鄰接矩陣中不存在的邊用INFINITY for (V = 0; V < Graph->Nv ; V++){ dist[V] = Graph->G[S][V]; if (dist[V] < INFINITY) path[V] = S;//將S周圍一圈的鄰接點全賦予父頂點S else path[V] = -1; collected[V] = false; } //先將源點S收入集合中 dist[S] = 0;//path[S] = -1 collected[S] = true; while (1){ //V=未被收錄頂點中dist最小者 V = FindMinDist(Graph, dist, collected); if (V == -1) break;//演算法結束 collected[V] = true; for (W = 0; W < Graph->Nv ; W++){//對於圖中每一個頂點W if (collected[W] == false && Graph->G[V][W] < INFINITY){ //若W是V的鄰接點並且W未被收錄 if (Graph->G[V][W] < 0) return false;//若有負值圈,則不能正確解決,返回錯誤標記 if (dist[V] + Graph->G[V][W] < dist[W]){ //該情況為,V進入集合S後可能影響另外一個W的dist,且隻影響V周圍一圈鄰接點的dist值 dist[W] = dist[V] + Graph->G[V][W];//更新dist[W] path[W] = V;//更新S到W的路徑,即增加W的父頂點V } } } } return true;//演算法執行完畢,返回正確標記 } void PrintPath(int *path, Vertex W, Vertex S) //列印最短路徑經過的所有頂點,此處列印0(path[0]=-1)到每個頂點的最短路徑 //先逆序push,再順序pop { stack<int> s; s.push(W);//path[W] = V,path[S] = -1 while (W != S){ W = path[W]; s.push(W) ; }//W = S時跳出迴圈,即到達源點 while (!s.empty() ){ if (s.size() == 1 ) printf("%d", s.top() ); else printf("%d ", s.top() ); s.pop() ; } } int main() { MGraph Graph; int VertexNum, EdgeNum; scanf("%d %d", &VertexNum, &EdgeNum); int *dist = (int *)malloc(sizeof (int) * VertexNum);//初始化dist int *path = (int *)malloc(sizeof (int) * VertexNum);//初始化path Graph = CreateGraph(VertexNum); Graph->Ne = EdgeNum; Edge E = (Edge)malloc(sizeof(struct ENode )); for(int i = 0; i < Graph->Ne ; i++){ scanf("%d %d %d", &E->V1 , &E->V2 , &E->Weight ); InsertEdge(Graph, E); }//至此,圖建立完成 //下面進入Dijkstra演算法部分 int S = 0;//定義源點S,Graph->G[0][0]=INFINITY Dijkstra(Graph, dist, path, S); //PrintPath(path); for (int i = 0; i < VertexNum; i++){ if (i != S){ PrintPath(path, i, S); printf(" %d\n", dist[i]); } } return 0; }
  • 測試資料: 在這裡插入圖片描述
  • 測試結果: 在這裡插入圖片描述

Reference