迪傑斯特拉算法(第七章 P188 算法7.15)

對於給定的有向圖G=(V,E)及單個源點Vs,求Vs到G的其餘各頂點的最短路徑。    

針對單源點的最短路徑問題,Dijkstra 提出了一種按路徑長度遞增次序產生最短路徑的算法,即迪傑斯特拉(Dijkstra)算法。

 

基本思想    

從圖的給定源點到其它各個頂點之間客觀上應存在一條最短路徑,在這組最短路徑中,按其長度的遞增次序,依次求出到不同頂點的最短路徑和路徑長度。即按長度遞增的次序生成各頂點的最短路徑,即先求出長度最小的一條最短路徑,然後求出長度第二小的最短路徑,依此類推,直到求出長度最長的最短路徑。

 

算法思想說明    

設給定源點爲Vs,S爲已求得最短路徑的終點集,開始時令S={Vs} 。

當求得第一條最短路徑(Vs ,Vi)後,S爲{Vs,Vi} 。根據以下結論可求下一條最短路徑。    

設下一條最短路徑終點爲Vj ,則Vj只有:

  1. ◆  源點到終點有直接的弧<Vs,Vj>;
  2. ◆ 從Vs 出發到Vj 的這條最短路徑所經過的所有中間頂點必定在S中。即只有這條最短路徑的最後一條弧纔是從S內某個頂點連接到S外的頂點Vj 。

 

若定義一個數組 dist[n],其每個 dist[i] 分量保存從Vs 出發中間只經過集合 S 中的頂點而到達 Vi 的所有路徑中長度最小的路徑長度值,則下一條最短路徑的終點 Vj 必定是不在 S 中且值最小的頂點,即: dist[i]=Min{ dist[k]| Vk∈V-S }        

利用上述公式就可以依次找出下一條最短路徑。

 

 

算法步驟

 

① 令S={Vs} ,用帶權的鄰接矩陣表示有向圖,對圖中每個頂點 Vi 按以下原則置初值:

② 選擇一個頂點Vj ,使得:

 

, Vj就是求得的下一條最短路徑終點,將Vj 併入到S中,即S=S∪{Vj} 。

③ 對 V-S 中的每個頂點 Vk ,修改dist[k],方法是: 

④ 重複②,③,直到S=V爲止。

 

對下圖的帶權有向圖,用 Dijkstra 算法求從頂點 0 到其餘各頂點的最短路徑,數組 dist 和 pre 的各分量的變化如下表所示。

 

算法分析          

Dijkstra算法的主要執行是:

  1. ◆ 數組變量的初始化:時間複雜度是O(n) ;
  2. ◆ 求最短路徑的二重循環:時間複雜度是O(n2) ;        

因此,整個算法的時間複雜度是O(n2) 。        

 

算法實現    

用帶權的鄰接矩陣表示有向圖, 對Prim算法略加改動就成了Dijkstra算法,將Prim算法中求每個頂點 Vk 的 lowcost 值用 dist[k] 代替即可。

◆  設數組 pre[n] 保存從 Vs 到其它頂點的最短路徑。若 pre[i]=k,表示從 Vs 到 Vi 的最短路徑中,Vi 的前一個頂點是 Vk,即最短路徑序列是(Vs , …, Vk  , Vi) 。

◆ 設數組 final[n],標識一個頂點是否已加入S中。

 

typedef int Status; /* Status是函數的類型,其值是函數結果狀態代碼,如OK等 */
typedef int Boolean; /* Boolean是布爾類型,其值是TRUE或FALSE */

#include<malloc.h> /* malloc()等 */
#include<stdio.h> /* EOF(=^Z或F6),NULL */
#include<process.h> /* exit() */
#include<limits.h> //常量INT_MAX和INT_MIN分別表示最大、最小整數

/* 函數結果狀態代碼 */
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2 


#define MAX_NAME 5 /* 頂點字符串的最大長度+1 */
#define MAX_INFO 20 /* 相關信息字符串的最大長度+1 */
typedef int VRType;
typedef char InfoType;
typedef char VertexType[MAX_NAME];


/* ---------------------------------  圖的數組(鄰接矩陣)存儲表示    --------------------------------*/

#define INFINITY INT_MAX /* 用整型最大值代替∞ */
#define MAX_VERTEX_NUM 20 /* 最大頂點個數 */
typedef enum { DG, DN, AG, AN }GraphKind; /* {有向圖,有向網,無向圖,無向網} */
typedef struct
{
	VRType adj; /* 頂點關係類型。對無權圖,用1(是)或0(否)表示相鄰否; */
			/* 對帶權圖,c則爲權值類型 */
	InfoType *info; /* 該弧相關信息的指針(可無) */
}ArcCell, AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
typedef struct
{
	VertexType vexs[MAX_VERTEX_NUM]; /* 頂點向量 */
	AdjMatrix arcs; /* 鄰接矩陣 */
	int vexnum, arcnum; /* 圖的當前頂點數和弧數 */
	GraphKind kind; /* 圖的種類標誌 */
}MGraph;

/* ---------------------------------------------------------------------------------------------*/

typedef int PathMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
typedef int ShortPathTable[MAX_VERTEX_NUM];




/* ---------------------------  需要用的圖的數組(鄰接矩陣)存儲的基本操作 --------------------------*/


int LocateVex(MGraph G, VertexType u)
{ /* 初始條件:圖G存在,u和G中頂點有相同特徵 */
  /* 操作結果:若G中存在頂點u,則返回該頂點在圖中位置;否則返回-1 */
	int i;
	for (i = 0; i < G.vexnum; ++i)
		if (strcmp(u, G.vexs[i]) == 0)
			return i;
	return -1;
}

Status CreateDN(MGraph *G)
{ /* 採用數組(鄰接矩陣)表示法,構造有向網G */
	int i, j, k, w, IncInfo;
	char s[MAX_INFO], *info;
	VertexType va, vb;
	printf("請輸入有向網G的頂點數,弧數,弧是否含其它信息(是:1,否:0): ");
	scanf("%d,%d,%d", &(*G).vexnum, &(*G).arcnum, &IncInfo);
	printf("請輸入%d個頂點的值(<%d個字符):\n", (*G).vexnum, MAX_NAME);
	for (i = 0; i < (*G).vexnum; ++i) /* 構造頂點向量 */
		scanf("%s", (*G).vexs[i]);
	for (i = 0; i < (*G).vexnum; ++i) /* 初始化鄰接矩陣 */
		for (j = 0; j < (*G).vexnum; ++j)
		{
			(*G).arcs[i][j].adj = INFINITY; /* 網 */
			(*G).arcs[i][j].info = NULL;
		}
	printf("請輸入%d條弧的弧尾 弧頭 權值(以空格作爲間隔): \n", (*G).arcnum);
	for (k = 0; k < (*G).arcnum; ++k)
	{
		scanf("%s%s%d%*c", va, vb, &w);  /* %*c吃掉回車符 */
		i = LocateVex(*G, va);
		j = LocateVex(*G, vb);
		(*G).arcs[i][j].adj = w; /* 有向網 */
		if (IncInfo)
		{
			printf("請輸入該弧的相關信息(<%d個字符): ", MAX_INFO);
			gets(s);
			w = strlen(s);
			if (w)
			{
				info = (char*)malloc((w + 1) * sizeof(char));
				strcpy(info, s);
				(*G).arcs[i][j].info = info; /* 有向 */
			}
		}
	}
	(*G).kind = DN;
	return OK;
}


/* --------------------------------------------------------------------------------------------------*/



/*  實現算法7.15的程序。迪傑斯特拉算法的實現 */


void ShortestPath_DIJ(MGraph G, int v0, PathMatrix *P, ShortPathTable *D)
{ /* 用Dijkstra算法求有向網G的v0頂點到其餘頂點v的最短路徑P[v]及帶權長度 */
  /* D[v]。若P[v][w]爲TRUE,則w是從v0到v當前求得最短路徑上的頂點。 */
  /* final[v]爲TRUE當且僅當v∈S,即已經求得從v0到v的最短路徑 算法7.15 */
	int v, w, i, j, min;
	Status final[MAX_VERTEX_NUM];
	for (v = 0; v < G.vexnum; ++v)
	{
		final[v] = FALSE;
		(*D)[v] = G.arcs[v0][v].adj;
		for (w = 0; w < G.vexnum; ++w)
			(*P)[v][w] = FALSE; /* 設空路徑 */
		if ((*D)[v] < INFINITY)
		{
			(*P)[v][v0] = TRUE;
			(*P)[v][v] = TRUE;
		}
	}
	(*D)[v0] = 0;
	final[v0] = TRUE; /* 初始化,v0頂點屬於S集 */
	for (i = 1; i < G.vexnum; ++i) /* 其餘G.vexnum-1個頂點 */
	{ /* 開始主循環,每次求得v0到某個v頂點的最短路徑,並加v到S集 */
		min = INFINITY; /* 當前所知離v0頂點的最近距離 */
		for (w = 0; w < G.vexnum; ++w)
			if (!final[w]) /* w頂點在V-S中 */
				if ((*D)[w] < min)
				{
					v = w;
					min = (*D)[w];
				} /* w頂點離v0頂點更近 */
		final[v] = TRUE; /* 離v0頂點最近的v加入S集 */
		for (w = 0; w < G.vexnum; ++w) /* 更新當前最短路徑及距離 */
		{
			if (!final[w] && min < INFINITY&&G.arcs[v][w].adj < INFINITY && (min + G.arcs[v][w].adj < (*D)[w]))
			{ /* 修改D[w]和P[w],w∈V-S */
				(*D)[w] = min + G.arcs[v][w].adj;
				for (j = 0; j < G.vexnum; ++j)
					(*P)[w][j] = (*P)[v][j];
				(*P)[w][w] = TRUE;
			}
		}
	}
}

void main()
{
	int i, j, v0 = 0; /* v0爲源點 */
	MGraph g;
	PathMatrix p;
	ShortPathTable d;
	CreateDN(&g);
	ShortestPath_DIJ(g, v0, &p, &d);
	printf("最短路徑數組p[i][j]如下:\n");
	for (i = 0; i < g.vexnum; ++i)
	{
		for (j = 0; j < g.vexnum; ++j)
			printf("%2d", p[i][j]);
		printf("\n");
	}
	printf("%s到各頂點的最短路徑長度爲:\n", g.vexs[0]);
	for (i = 1; i < g.vexnum; ++i)
		printf("%s-%s:%d\n", g.vexs[0], g.vexs[i], d[i]);
}

運行結果:

有向圖:

運行過程圖示:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章