迪杰斯特拉算法(第七章 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]);
}

运行结果:

有向图:

运行过程图示:

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值