图的Dijkstra算法——C/C++

例子:
在这里插入图片描述

一、算法原理

1.首先,引入一个辅助向量 dist[],它的每个分量 dist[] 表示当前所找到的,从起始点(即源点)到其它每个顶点的长度。
例如,dist[6] = 16表示从起始点到顶点3的路径相对最小长度为2。这里强调相对就是说在算法执行过程中 dist[] 的值是在不断逼近最终结果但在过程中不一定就等于长度。

2.dist[] 的初始状态为0:若从 v 到 vj 有边,则 dist[] 为边上的权值,否则置dist[]为∞。
显然,长度为 dist[j] = Min{ dist[] | vi∈V } 的路径就是从 v 出发到顶点 vj 的长度最短的一条路径,路径为(v,vj)。

3.那么,下一条长度次短的是哪一条呢?也就是找到从源点到下一个顶点的最短路径长度所对应的顶点,且这条最短路径长度仅次于从源点到顶点的最短路径长度。
假设该次短路径的终点是 vk,则可想而知,这条路径要么是(v,vk),或者是(v,vj,vk)。它的长度就是最小的路径长度。

4.一般情况下,假设S为已求得的从源点出发的最短路径长度的顶点的集合,则可证明:下一条次最短路径(设其终点为 x)要么是(v,x),或者是从源点出发的中间只经过S中的顶点而最后到达顶点的路径。因此,下一条长度次短的的最短路径长度必是 dist[]= Min{ dist[]| vi ∈V-S },其中 dist[] 要么是(v,vi)上的权值,或者弧(v,vk,vi)上的权值之和。

二、算法思想

1、 初始时令 S={V0},U=V-S={其余顶点},T中顶点对应的距离值
若存在<V0,Vi>,d(V0,Vi)为<V0,Vi>弧上的权值
若不存在<V0,Vi>,d(V0,Vi)为∞
2.、从T中选取一个与S中顶点有关联边且权值最小的顶点W,加入到S中
3.、对其余T中顶点的距离值进行修改:若加进W作中间顶点,从V0到Vi的距离值缩短,则修改此距离值
重复上述步骤2、3,直到S中包含所有顶点,即W=Vi为止
在这里插入图片描述

三、算法实现

1、Dijkstra算法

void Dijkstra(MatGraph LG, int v) {
	int dist[MAXV];									//存储权值
	int path[MAXV];									//是否有路径
	int S[MAXV];									//S[i]==0表示顶点 i 在S中, S[i]==1 表示 顶点 i 在 U 中
	int MinDis;										//最小的权值
	int u = 0;										//记录最短路径的顶点
	int i, j;
	for (i = 0; i < LG.n; i++) {
		dist[i] = LG.adjMat[v][i];					//距离初始化
		S[i] = 0;									//S[] 初始化
		if (LG.adjMat[v][i] < 32767) {				//路径初始化
			path[i] = v;							//顶点 v 到顶点 i 有边时,置顶点 i 的前一个顶点为 v
		}
		else {
			path[i] = -1;							//顶点 v 到顶点 i 无边时,置顶点 i 的前一个顶点为 -1
		}
	}
	S[v] = 1;										//源点编号 v 放入 S
	path[v] = 0;									
	for (i = 0; i < LG.n - 1; i++) {				//循环求 v 到所有顶点的最短路径
		MinDis = INF;								//置最大长度初值
		for (j = 0; j < LG.n; j++) {
			if (S[j] == 0 && dist[j] < MinDis) {	//选取不在 S 中(即 U 中)且就有路径长度的顶点 u
				u = j;
				MinDis = dist[j];					//得到顶点、路径长度
			}
		}
		S[u] = 1;									//顶点 u 加入 S
		for (j = 0; j < LG.n; j++) {				//修改最短路径
			if (S[j] == 0) {
				if (LG.adjMat[u][j] < INF && dist[u] + LG.adjMat[u][j] < dist[j]) {
					dist[j] = dist[u] + LG.adjMat[u][j];
					path[j] = u;
				}
			}
		}
	}
	displayPath(LG, dist, path, S, v);				//输出最短路径
}

2、输出单源最短路径

void displayPath(MatGraph LG, int dist[], int path[], int S[], int v) {
	int i, j, k;
	int aPath[MAXV];								//存放一条最短路径(逆向)
	int d;											//顶点个数
	for (i = 0; i < LG.n; i++) {					//循环输出从顶点 v 到 i 的路径
		if (S[i] == 1 && i != v) {
			printf("从顶点 %d 到顶点 %d 的路径长度为:%d\t 路径为:", v, i, dist[i]);
			d = 0;
			aPath[d] = i;							//添加路径上的终点
			k = path[i];
			if (k == -1) {							//没有路径
				printf("无路径\n");
			}
			else {									//存在路径就输出
				while (k != v) {
					d++;
					aPath[d] = k;
					k = path[k];
				}
				d++;
				aPath[d] = v;						//添加路径上的起点
				printf("%d ", aPath[d]);			//输出起点
				for (j = d - 1; j >= 0; j--) {		//输出其他顶点
					printf("%d ", aPath[j]);
				}
				printf("\n");
			}
		}
	}
}

四、整体框架

#include<stdio.h>
#include<malloc.h>

#define MAXV 7					//最大顶点个数 
#define INF 32767				//定义 ∞
//∞ == 32767 ,int 型的最大范围(2位)= 2^(2*8-1),TC告诉我们int占用2个字节,而VC和LGCC告诉我们int占用4个字节
//图:Graph
//顶点:Vertex
//邻接:Adjacency
//矩阵:Matrix
//表:List
//边:Edge 

typedef struct vertex {
	int number;					//顶点的编号 	
}VertexType; 					//别名,顶点的类型 

typedef struct matrix {
	int n;						//顶点个数
	int e;						//边数 
	int adjMat[MAXV][MAXV];		//邻接矩阵数组			
	VertexType ver[MAXV];		//存放顶点信息 
}MatGraph;						//别名,完整的图邻接矩阵类型

typedef struct eNode {
	int adjVer;					//该边的邻接点编号 
	int weiLGht;				//该边的的信息,如权值 
	struct eNode* nextEdLGe;	//指向下一条边的指针 
}EdgeNode; 						//别名,边结点的类型 

typedef struct vNode {
	EdgeNode* firstEdLGe;		//指向第一个边结点 
}VNode; 						//别名,邻接表的头结点类型 

typedef struct list {
	int n;						//顶点个数
	int e;						//边数
	VNode adjList[MAXV];		//邻接表的头结点数组 
}ListGraph;						//别名,完整的图邻接表类型 

//创建图的邻接表 
void createAdjListGraph(ListGraph* &LG, int A[MAXV][MAXV], int n, int e) {
	int i, j;
	EdgeNode* p;
	LG = (ListGraph*)malloc(sizeof(ListGraph));
	for (i = 0; i < n; i++) {
		LG->adjList[i].firstEdLGe = NULL;						//给邻接表中所有头结点指针域置初值 
	}
	for (i = 0; i < n; i++) {									//检查邻接矩阵中的每个元素 
		for (j = n - 1; j >= 0; j--) {
			if (A[i][j] != 0) {									//存在一条边 
				p = (EdgeNode*)malloc(sizeof(EdgeNode));		//申请一个结点内存
				p->adjVer = j;									//存放邻接点 
				p->weiLGht = A[i][j];							//存放权值
				p->nextEdLGe = NULL;

				p->nextEdLGe = LG->adjList[i].firstEdLGe;		//头插法 
				LG->adjList[i].firstEdLGe = p;
			}
		}
	}
	LG->n = n;
	LG->e = e;
}

//输出邻接表 
void displayAdjList(ListGraph* LG) {
	int i;
	EdgeNode* p;
	for (i = 0; i < MAXV; i++) {
		p = LG->adjList[i].firstEdLGe;
		printf("%d:", i);
		while (p != NULL) {
			if (p->weiLGht != 32767) {
				printf("%2d[%d]->", p->adjVer, p->weiLGht);
			}
			p = p->nextEdLGe;
		}
		printf(" NULL\n");
	}
}

//输出邻接矩阵
void displayAdjMat(MatGraph LG) {
	int i, j;
	for (i = 0; i < MAXV; i++) {
		for (j = 0; j < MAXV; j++) {
			if (LG.adjMat[i][j] == 0) {
				printf("%4s", "0");
			}
			else if (LG.adjMat[i][j] == 32767) {
				printf("%4s", "∞");
			}
			else {
				printf("%4d", LG.adjMat[i][j]);
			}
		}
		printf("\n");
	}
}

//邻接表转换为邻接矩阵
void ListToMat(ListGraph* LG, MatGraph& MG) {
	int i, j;
	EdgeNode* p;
	for (i = 0; i < MAXV; i++) {
		for (j = 0; j < MAXV; j++) {
			MG.adjMat[i][j] = 0;
		}
	}
	for (i = 0; i < LG->n; i++) {
		p = LG->adjList[i].firstEdLGe;
		while (p != NULL) {
			MG.adjMat[i][p->adjVer] = p->weiLGht;
			p = p->nextEdLGe;
		}
	}
	MG.n = LG->n;
	MG.e = LG->e;
}

//输出单源最短路径
void displayPath(MatGraph LG, int dist[], int path[], int S[], int v) {
	int i, j, k;
	int aPath[MAXV];								//存放一条最短路径(逆向)
	int d;											//顶点个数
	for (i = 0; i < LG.n; i++) {					//循环输出从顶点 v 到 i 的路径
		if (S[i] == 1 && i != v) {
			printf("从顶点 %d 到顶点 %d 的路径长度为:%d\t 路径为:", v, i, dist[i]);
			d = 0;
			aPath[d] = i;							//添加路径上的终点
			k = path[i];
			if (k == -1) {							//没有路径
				printf("无路径\n");
			}
			else {									//存在路径就输出
				while (k != v) {
					d++;
					aPath[d] = k;
					k = path[k];
				}
				d++;
				aPath[d] = v;						//添加路径上的起点
				printf("%d ", aPath[d]);			//输出起点
				for (j = d - 1; j >= 0; j--) {		//输出其他顶点
					printf("%d ", aPath[j]);
				}
				printf("\n");
			}
		}
	}
}

//Dijkstra算法
void Dijkstra(MatGraph LG, int v) {
	int dist[MAXV];									//存储权值
	int path[MAXV];									//是否有路径
	int S[MAXV];									//S[i]==0表示顶点 i 在S中, S[i]==1 表示 顶点 i 在 U 中
	int MinDis;										//最小的权值
	int u = 0;										//记录最短路径的顶点
	int i, j;
	for (i = 0; i < LG.n; i++) {
		dist[i] = LG.adjMat[v][i];					//距离初始化
		S[i] = 0;									//S[] 初始化
		if (LG.adjMat[v][i] < 32767) {				//路径初始化
			path[i] = v;							//顶点 v 到顶点 i 有边时,置顶点 i 的前一个顶点为 v
		}
		else {
			path[i] = -1;							//顶点 v 到顶点 i 无边时,置顶点 i 的前一个顶点为 -1
		}
	}
	S[v] = 1;										//源点编号 v 放入 S
	path[v] = 0;									
	for (i = 0; i < LG.n - 1; i++) {				//循环求 v 到所有顶点的最短路径
		MinDis = INF;								//置最大长度初值
		for (j = 0; j < LG.n; j++) {
			if (S[j] == 0 && dist[j] < MinDis) {	//选取不在 S 中(即 U 中)且就有路径长度的顶点 u
				u = j;
				MinDis = dist[j];					//得到顶点、路径长度
			}
		}
		S[u] = 1;									//顶点 u 加入 S
		for (j = 0; j < LG.n; j++) {				//修改最短路径
			if (S[j] == 0) {
				if (LG.adjMat[u][j] < INF && dist[u] + LG.adjMat[u][j] < dist[j]) {
					dist[j] = dist[u] + LG.adjMat[u][j];
					path[j] = u;
				}
			}
		}
	}
	displayPath(LG, dist, path, S, v);				//输出最短路径
}

int main() {
	ListGraph* LG;
	MatGraph MG;
	int array[MAXV][MAXV] = {
		{  0,  4,  6,  6,INF,INF,INF},
		{INF,  0,  1,INF,  7,INF,INF},
		{INF,INF,  0,INF,  6,  4,INF},
		{INF,INF,  2,  0,INF,  5,INF},
		{INF,INF,INF,INF,  0,INF,  6},
		{INF,INF,INF,INF,  1,  0,  8},
		{INF,INF,INF,INF,INF,INF,  0}
	};

	int e = 12;
	createAdjListGraph(LG, array, MAXV, e);
	displayAdjList(LG);
	printf("\n");

	ListToMat(LG, MG);
	displayAdjMat(MG);
	printf("\n");

	Dijkstra(MG, 0);
	printf("\n");

	return 0;
}

五、结果展示

在这里插入图片描述

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
好的,以下是基于 Dijkstra 算法实现武汉地铁站点最短路径搜索的 C++ 代码: ```cpp #include <iostream> #include <vector> #include <queue> #include <climits> using namespace std; const int MAXN = 100; // 最大站点数 const int INF = INT_MAX; // 无穷大 vector<pair<int, int>> adj[MAXN]; // 邻接表存 int dist[MAXN]; // 距离数组 bool vis[MAXN]; // 访问标记数组 // Dijkstra 算法实现 void dijkstra(int s) { dist[s] = 0; // 初始距离为0 priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> pq; pq.push(make_pair(dist[s], s)); while (!pq.empty()) { int u = pq.top().second; pq.pop(); if (vis[u]) continue; // 已经访问过了 vis[u] = true; for (pair<int, int> edge : adj[u]) { int v = edge.first; int w = edge.second; if (dist[v] > dist[u] + w) { dist[v] = dist[u] + w; // 更新距离 pq.push(make_pair(dist[v], v)); } } } } int main() { int n, m; cin >> n >> m; // 输入站点数和边数 for (int i = 0; i < m; i++) { int u, v, w; cin >> u >> v >> w; // 输入边的起点、终点、权值 adj[u].push_back(make_pair(v, w)); adj[v].push_back(make_pair(u, w)); // 无向 } int s, t; cin >> s >> t; // 输入起点和终点 fill(dist, dist + n, INF); // 初始化距离数组 dijkstra(s); // Dijkstra 算法求最短路径 cout << dist[t] << endl; // 输出起点到终点的最短距离 return 0; } ``` 在代码中,我们使用邻接表存储,并使用了 STL 中的优先队列来实现 Dijkstra 算法。同时,我们也注意到 Dijkstra 算法适用于边权值非负的,因此在实现中需要对边权值的范围做出限制。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值