有权图的单源最短路算法

有权图的单源最短路亦被称为Dijkstra算法(单源最短路径),首先要声明这个算法无法解决含有负值圈的图


显然最优解释围绕着红色的圈不停的兜圈,所以基本上所有的算法都挂掉了


排除掉这种情况之后

Dijkstra算法步骤如下:

G={V,E}
1. 初始时令 S={V0},T=V-S={其余顶点},T中顶点对应的距离值
        若存在<V0,Vi>,d(V0,Vi)为<V0,Vi>弧上的权值
        若不存在<V0,Vi>,d(V0,Vi)为∞
2. 从T中选取一个与S中顶点有关联边且权值最小的顶点W,加入到S中(每次从未被收录的顶点中选一个dist最小的收录 (贪心)
3. 对其余T中顶点的距离值进行修改:若加进W作中间顶点,从V0到Vi的距离值缩短,则修改此距离值

    重复上述步骤2、3,直到S中包含所有顶点,即W=Vi为止


可以得到证明的是:

若路径是按照递增(非递减)的顺序生成的,则

真正的最短路必须只经过S中的顶点

如果收录v使得s到w的路径变短,则s到w的路径一定经过v,并且v到w有一条边

增加一个v进入S,可能影响另一个w的dist值,w只可能是v一圈的点


下面我们对于下面的有权图举个例子

邻接矩阵的Dijkstra算法实现

下面是伪代码描述以及矩阵的初始化


其函数的C语言描述如下

/* IsEdge(Graph, V, W)检查<V, W>是否图Graph中的一条边,即W是否V的邻接点。  */
/* 此函数根据图的不同类型要做不同的实现,关键取决于对不存在的边的表示方法。*/
/* 例如对有权图, 如果不存在的边被初始化为INFINITY, 则函数实现如下:         */
bool IsEdge(MGraph Graph, Vertex V, Vertex W)
{
	return Graph->G[V][W] <INFINITY ? true : false;
}

/*邻接矩阵储存——有权图的单源最短路算法*/
Vertex FindMinDist(MGraph Graph) {
	/*返回未被收录顶点中的最小dist者*/
	Vertex MinV, V;
	int MinDist = INFINITY;

	for (V = 0; V < Graph->Nv; V++) {
		if (Visited[V] == false && dist[V] < MinDist) {
			/*若V未被收录,且dist[V]更小*/
			MinDist = dist[V];/*更新最小距离*/
			MinV = V;/*更新最小顶点*/
		}
	}
	if (MinDist < INFINITY)/*若找到最小dist*/
		return MinV;/*返回对应的顶点下标*/
	else return ERROR;/*若这样的顶点不存在,返回错误标记*/
}


/* dist[]和path[]为全局变量,已经初始化为-1 */
bool Dijkstra(MGraph Graph, Vertex S)
{   /* 以S为出发点对邻接矩阵存储的图Graph进行BFS搜索 */
	Vertex V, W;

	/*初始化:此处默认邻接矩阵中不存在的边用INFINITY表示*/
	for (V = 0; V < Graph->Nv; V++) {
		dist[V] = Graph->G[S][V];
		if (dist[V] < INFINITY)
			path[V] = S;
		else
			path[V] = -1;
		    Visited[V] = false;
	}
	/*先将起点收入集合*/
	dist[S] = 0;
	Visited[S] = true;
	
	while (1)
	{
		/*找到未被收入顶点中dist最小者*/
		V = FindMinDist(Graph);
		if (V == ERROR)/*若这样的V不存在*/
			break;//算法结束
		Visited[V] = true;//收录V
		for (W = 0; W < Graph->Nv; W++)//对图中每个顶点W
			if (Visited[W] == false && Graph->G[V][W] < INFINITY) {//若W未被收录并且是V的临近点
				if (Graph->G[V][W] < 0)//若有负边
					return false;//不能正确解决,返回错误标记
				if (dist[V] + Graph->G[V][W] < dist[W])//若收录V使得dist[W]变小
				{
					dist[W] = dist[V] + Graph->G[V][W];//更新dist[W]
					path[W] = V;//更新S到W的路径
				}
			}
	}/*while结束*/
	return true;/*算法执行完毕,返回正确标记*/
}
执行结果嘿嘿,当然没毛病咯


和我们通过肉眼看的结果一样一样的


附上完整代码

#include <stdio.h>        
#include <stdlib.h>        

#define ERROR 0        
#define INFINITY 65535        /* ∞设为双字节无符号整数的最大值65535*/        
#define MaxVertexNum 100    /* 最大顶点数设为100 */        
typedef int Vertex;         /* 用顶点下标表示顶点,为整型 */
typedef int WeightType;        /* 边的权值设为整型 */
typedef char DataType;        /* 顶点存储的数据类型设为字符型 */

int Visited[MaxVertexNum];
int dist[MaxVertexNum];
int path[MaxVertexNum];


/* 边的定义 */
typedef struct ENode *PtrToENode;
struct ENode {
	Vertex V1, V2;      /* 有向边<V1, V2> */
	WeightType Weight;  /* 权重 */
};
typedef PtrToENode Edge;

/* 图结点的定义 */
typedef struct GNode *PtrToGNode;
struct GNode {
	int Nv;  /* 顶点数 */
	int Ne;  /* 边数   */
	WeightType G[MaxVertexNum][MaxVertexNum]; /* 邻接矩阵 */
};
typedef PtrToGNode MGraph; /* 以邻接矩阵存储的图类型 */



MGraph CreateGraph(int VertexNum)
{ /* 初始化一个有VertexNum个顶点但没有边的图 */
	Vertex V, W;
	MGraph Graph;

	Graph = (MGraph)malloc(sizeof(struct GNode)); /* 建立图 */
	Graph->Nv = VertexNum;
	Graph->Ne = 0;
	/* 初始化邻接矩阵 */
	/* 注意:这里默认顶点编号从0开始,到(Graph->Nv - 1) */
	for (V = 0; V<Graph->Nv; V++)
		for (W = 0; W<Graph->Nv; W++)
			Graph->G[V][W] = INFINITY;

	return Graph;
}

void InsertEdge(MGraph Graph, Edge E)
{
	/* 插入边 <V1, V2> */
	Graph->G[E->V1][E->V2] = E->Weight;
	/* 若是无向图,还要插入边<V2, V1> */
	//Graph->G[E->V2][E->V1] = 1;    
}

/* IsEdge(Graph, V, W)检查<V, W>是否图Graph中的一条边,即W是否V的邻接点。  */
/* 此函数根据图的不同类型要做不同的实现,关键取决于对不存在的边的表示方法。*/
/* 例如对有权图, 如果不存在的边被初始化为INFINITY, 则函数实现如下:         */
bool IsEdge(MGraph Graph, Vertex V, Vertex W)
{
	return Graph->G[V][W] <INFINITY ? true : false;
}

/*邻接矩阵储存——有权图的单源最短路算法*/
Vertex FindMinDist(MGraph Graph) {
	/*返回未被收录顶点中的最小dist者*/
	Vertex MinV, V;
	int MinDist = INFINITY;

	for (V = 0; V < Graph->Nv; V++) {
		if (Visited[V] == false && dist[V] < MinDist) {
			/*若V未被收录,且dist[V]更小*/
			MinDist = dist[V];/*更新最小距离*/
			MinV = V;/*更新最小顶点*/
		}
	}
	if (MinDist < INFINITY)/*若找到最小dist*/
		return MinV;/*返回对应的顶点下标*/
	else return ERROR;/*若这样的顶点不存在,返回错误标记*/
}


/* dist[]和path[]为全局变量,已经初始化为-1 */
bool Dijkstra(MGraph Graph, Vertex S)
{   /* 以S为出发点对邻接矩阵存储的图Graph进行BFS搜索 */
	Vertex V, W;

	/*初始化:此处默认邻接矩阵中不存在的边用INFINITY表示*/
	for (V = 0; V < Graph->Nv; V++) {
		dist[V] = Graph->G[S][V];
		if (dist[V] < INFINITY)
			path[V] = S;
		else
			path[V] = -1;
		Visited[V] = false;
	}
	/*先将起点收入集合*/
	dist[S] = 0;
	Visited[S] = true;

	while (1)
	{
		/*找到未被收入顶点中dist最小者*/
		V = FindMinDist(Graph);
		if (V == ERROR)/*若这样的V不存在*/
			break;//算法结束  
		Visited[V] = true;//收录V  

		for (W = 0; W < Graph->Nv; W++)//对图中每个顶点W  
			if (Visited[W] == false && Graph->G[V][W] < INFINITY) {//若W未被收录并且是V的临近点  
				if (Graph->G[V][W] < 0)//若有负边  
					return false;//不能正确解决,返回错误标记  
				if (dist[V] + Graph->G[V][W] < dist[W])//若收录V使得dist[W]变小  
				{
					dist[W] = dist[V] + Graph->G[V][W];//更新dist[W]  
					path[W] = V;//更新S到W的路径  
				}
			}
	}/*while结束*/
	return true;/*算法执行完毕,返回正确标记*/
}

int main() {
	MGraph Graph = CreateGraph(7);
	Graph->Ne = 12;
	Vertex a[12] = { 0,0,1,1,2,2,3,3,3,3,4,6 };
	Vertex b[12] = { 1,3,3,4,0,5,2,4,5,6,6,5 };
	WeightType w[12] = { 2,1,3,10,4,5,2,2,8,4,6,1 };

	/* 读入边,格式为"起点 终点 权重",插入邻接矩阵 */
	for (int i = 0; i<Graph->Ne; i++) {
		Edge E = (Edge)malloc(sizeof(struct ENode)); /* 建立边结点 */
		E->V1 = a[i]; E->V2 = b[i]; E->Weight = w[i];
		/* 注意:如果权重不是整型,Weight的读入格式要改 */
		InsertEdge(Graph, E);
	}


	for (int i = 0; i < MaxVertexNum; i++) { Visited[i] = false; dist[i] = INFINITY; path[i] = -1; }
	Dijkstra(Graph, 0);
	int i = 0;
	for (int i = 0; i < Graph->Nv; i++) {
		printf("点%d到点1的最小距离是:%d  上一步结点是:%d\n", i + 1, dist[i], path[i] + 1);
	}
	system("pause");
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

拉风小宇

请我喝个咖啡呗

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值