[数据结构/图]dijkstra单源最短路径算法实现

简单介绍

dijkstra算法是图论中一个求单源最短路径的经典算法,通过逐步并入与当前已并入点相连接的最短路径的一个点,来找到起点和终点之间最短的路径。基本原理已经有很多博主阐述过了,不理解过程的安利去看天勤的数据结构这部分,很清楚。这里主要就c/c++语言的代码做一个详细讲解,代码亲测可以运行,从博主个人的理解出发,希望能帮到不理解代码过程的朋友!

代码讲解

1.数据结构及输入输出

这里采用图的邻接矩阵表示法:

typedef struct {
	int v, e;//分别为点的数量、边的数量
	int w[MAX][MAX];//权值
}Graph;

输入采用输入点和边的数量,在对边的信息进行输入,格式为“起点,终点,权值”

例如:表示一张五个点七条边的有向图

5 7
0 1 2
0 2 4
1 2 1
1 3 7
2 3 3
2 4 5
3 4 1

 2.变量声明

我们需要定义一个dist[]数组,表示当前起点到其余各个顶点的最短路径,path[]数组存放记录的路径,存放的是前一个途经点,set[]数组表示当前并入的顶点集合,已经并入记为1。这里定义MAX和INF的值都为105。

 3.dijkstra函数体

传入各个参数,v为起点。思路过程为:

(1)先初始化,对于从起点到其余每一个点的边进行遍历

  ①dist数组的初始值为图的初始值

  ②set[]数组初始值为0 

  ③path数组,对于初始情况来说,如果这个边是通的,那说明最短路径这条路上的第一个点是起点,所以把v放入,如果这条边的长度为初始的INF,那说明起点到不了这里,没有上一个点,值赋为-1即可

(2)再纳入起点,set[v]=1
(3)再用一层for进行点数-1次循环处理剩余点

  因为除了起点之外,还剩v-1个点,这块处理的思路是在与起点相连的点里面先找一个最短距离的点,作为扩展点。需要在外层for循环里面再套一层for循环。定义一个min最小值初始化为INF,再遍历dist数组,在这个点没有被访问过(set值不为1)且这个dist记录比最小值还小的时候,记录最小值,并记录最小值下标,就找到了下一个要被纳入的点的下标。

(4)纳入新点
(5)更新dist数组

 这一步是最为关键的一步,新纳入点之后,当前起点到各个点的最短路径就可能发生变化,所以需要再次遍历一遍,当没有被访问过,且记录的路径比越过新的点的路径之和要小的时候,更新更小的那个值,dist[i]表示当前记录的从起点到i点最短路径,dist[u]表示从起点到u点(新并入的点)的最短路径,w[u][i]表示u点到i点这条边的权值,所以表示为代码就是dist[i] > dist[u] + g.w[u][i]

记录下那个最小值,说明走u点会使这条路更短,就把u放在path记录里面,更新path数组。

void dijkstra(int path[], int dist[],Graph g, int v) {
	int set[MAX];
	for (int i = 0; i < g.v; i++) {
		dist[i] = g.w[v][i];//初始化dist数组
		set[i] = 0;
		if (g.w[v][i] < INF) {
			//如果这个边可以走,那么把起点放在path路径上
			path[i] = v;
		}
		else {
			path[i] = -1;//否则说明它没有起点
		}
	}//初始化结束
	set[v] = 1;//纳入起点
	for (int i = 0; i < g.v - 1; i++) {
		int min = INF; int u=0;
		for (int j = 0; j < g.v; j++) {
			//dist数组的含义为当前点到各个点的最短距离,当前初始化为起点到各个点
			//在当前这组dist数中找最小的一个作为拓展
			if (set[j] != 1 && dist[j] < min) {
				min = dist[j];//找到当前最小值
				u = j;//记录最小的下标
			}
		}
		set[u]=1;//纳入最小值的点
		for (int i = 0; i < g.v; i++) {
			if (set[i] ==0 && dist[i] > dist[u] + g.w[u][i]) {
				dist[i] = dist[u] + g.w[u][i];//看当前存入的从起点到当前点远还是从起点到刚刚并入的点+刚刚并入的点到其当前点远
				path[i] = u;//更新path路径为上一个并入的点
			}
		}
	}
}

 4.traceback函数

  这个函数用来回溯路径,因为path中记录了上一个点,所以不断递归调用即可,当值为-1时说明前面没有点了,到头了,return就行。

void traceback(int path[], int x, int y) {
	//回溯从x点到y点的最短路径途径的顶点
	if (path[y] == -1) {
		return;//相当于不断的读取上一步的最短点是谁,当回溯到头的时候直接跳出
	}
	traceback(path, x, path[y]);//回溯上一步
	cout << y<<endl;//打印点
}
5.全部代码展示
#define MAX 105

int INF = 105;
typedef struct {
	int v, e;
	int w[MAX][MAX];
}Graph;
void dijkstra(int path[], int dist[],Graph g, int v) {
	int set[MAX];
	for (int i = 0; i < g.v; i++) {
		dist[i] = g.w[v][i];//初始化dist数组
		set[i] = 0;
		if (g.w[v][i] < INF) {
			//如果这个边可以走,那么把起点放在path路径上
			path[i] = v;
		}
		else {
			path[i] = -1;//否则说明它没有起点
		}
	}//初始化结束
	set[v] = 1;//纳入起点
	for (int i = 0; i < g.v - 1; i++) {
		int min = INF; int u=0;
		for (int j = 0; j < g.v; j++) {
			//dist数组的含义为当前点到各个点的最短距离,当前初始化为起点到各个点
			//在当前这组dist数中找最小的一个作为拓展
			if (set[j] != 1 && dist[j] < min) {
				min = dist[j];//找到当前最小值
				u = j;//记录最小的下标
			}
		}
		set[u]=1;//纳入最小值的点
		for (int i = 0; i < g.v; i++) {
			if (set[i] ==0 && dist[i] > dist[u] + g.w[u][i]) {
				dist[i] = dist[u] + g.w[u][i];//看当前存入的从起点到当前点远还是从起点到刚刚并入的点+刚刚并入的点到其当前点远
				path[i] = u;//更新path路径为上一个并入的点
			}
		}
	}
}
void traceback(int path[], int x, int y) {
	//回溯从x点到y点的最短路径途径的顶点
	if (path[y] == -1) {
		return;//相当于不断的读取上一步的最短点是谁,当回溯到头的时候直接跳出
	}
	traceback(path, x, path[y]);//回溯上一步
	cout << y<<endl;//打印点
}

int main() {
	Graph g;
	int path[MAX]; int dist[MAX];
	int v, e; int a, b, c; int x, y;
	cin >> v >> e;//输入点和边
	g.v = v; g.e = e;
	for (int i = 0; i < v; i++) {
		for (int j = 0; j < v; j++) {
			//初始化邻接矩阵
			g.w[i][j] = INF;
		}
	}
	for (int i = 0; i < e; i++) {
		cin >> a >> b >> c;
		g.w[a][b] = c;
	}

	dijkstra(path, dist, g, 0);//以0为起点
	cout << "请输入查询点";
	cin >> x >> y;
	traceback(path, x, y);

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值