数据结构学习之——最短路径(Dijkstra算法)

最短路径问题(Dijkstra算法)

先前的文章里介绍了两种图的遍历的方式,解决问题的能力也很有限,只能通过图向主人公反馈一个是否可以成功跳出水坑的结果,路径即使可以给出,或许也不是真正的最短路径。那要获取正真的最短路径,需要引入一种新的算法。

要解决的实例

先回顾一下先前的问题
例子是主人公站在水坑的中心点P1,水坑的长宽为5米,水坑散布着位置随机的砖块若干,主人公一次可以跳跃1米的长度来到达下一块砖,需要判断随机生成的点是否可以帮助主人公离开水坑。并且要求能给出最短的路径。
在这里插入图片描述
此图为随机生成的落脚点

程序设计思路

我们需要不断的确定距离中心点路径最短的点以及它距离中点的距离。
因此,对于每一个点,我们需要创建一个这样的结构:
参数1:distance,该点到中心点的最小路径
参数2:pre,该点到中点的最小路径经过的上一个点
参数3:seen,该点是否为我们已经处理过的点(处理的问题接下来讲)
初始化参数:我们需要将所有的结点以及中心点都放入到一个数组或者集合之中,方便我们对参数经行初始化。
参数初始化
1.需要将所有的distance都更新为无穷大,因为我们在后序计算中会根据路径的变化不断地跟新这个最小值,所以期望在预设时将其设定为无穷大。
2.pre可以设为空。
3.seen只是一个是否处理的标志位,可以设为-1。
定义好以上结构后我们需要创建我们用来收录这些结点的盒子,其实也可以不用,但是有可以帮助理解。
算法核心:
1.将原点放入盒子中,计算与原点直接相连的结点距离原点的距离写入distance。
2.选出所有节点中,除了原点和已经放入盒子的结点之外,与原点距离最近的结点。
3.计算该结点与其所有的未被收录的邻接点之间的距离dist1。并加上该结点的distance,若dist1+distance<邻接点的distance(这里体现为什么要将distance初始化为无穷大),则将该dist1+distance更新到该邻接点的distance。并将该邻接点的pre指向该结点。
重复经行2,3操作,直到所有结点都被收录到盒子中,则结束循环。

程序思路是这样,但是我们应当如何处理我们的实际问题。
在我们的实际问题中,因为结点的随机性,其实很多结点并不会被收录到盒子中,这些结点所在的连通分量可能并不包含中心点,因为我们限制了跳跃一步的距离只能是1m。所以有很多点可能根本无法涉足。
即使是这样也不妨碍我们利用上述思路解决问题。只是在选择邻接点时需要加上限制条件,距离需要小于1m。
最后可能还有一个问题就是我现在知道了中心点所在的连通分量中所有结点距离中心点的最短路径的距离以及这些结点的最短路径中上一个结点是哪一个。那么我如何确定哪一条是我可以跳出水坑的最短路径呢?
我们只需要分析盒子中的点中,哪些点可以跳出水坑,并且距离岸边的长度加上本身距离中心点的长度,就是该结点所在的最短路径的长度。只需要比较这些点找到其中最短的那个,便能得到最短路径以及它的长度了。

程序源码

//有权图的最短路径
void Dijkstra(void) {
	int i;
	int god;
	int Flag=-1;
	int min;
	struct Pos *pa;
	//初始化原点的参数
	P1->Stand->distance = 0;
	P1->Stand->seen = -1;
	P1->Stand->Pre = NULL;
	//寻找关于原点的邻接点
	for (i = 1; i <= FOOTHOLD; i++) {
		god = ((map->Position[i].x) - (P1->Stand->x))*((map->Position[i].x) - (P1->Stand->x)) + ((map->Position[i].y) - (P1->Stand->y))*((map->Position[i].y) - (P1->Stand->y));
		god = (int)sqrt(god);
		if (god < 10) {
			map->Position[i].Pre = P1->Stand;
			map->Position[i].distance = god;
			printf("[%d][%d] d:%d ", map->Position[i].x, map->Position[i].y, map->Position[i].distance);
		}
	}
	while (1) {
		min = 0xFFFF;
		Flag = -1;
		//寻找未收录的定点中dist最小的
		for (i = 1; i <= FOOTHOLD; i++) {
			//如果该点已被收录,则跳过该点
			if (map->Position[i].seen == 1) {
				continue;
			}
			if (map->Position[i].distance < min) {
				min = map->Position[i].distance;
				Flag = i;
			}
		}
		//若不存在未被收录的顶点,则跳出循环
		if (Flag == -1) {
			printf("Finished!");
			for (i = 1; i <= FOOTHOLD; i++) {
				min = 10;
				if (map->Position[i].seen == 1) {
					if (map->Position[i].x < min) {
						min = map->Position[i].x;
					}
					if ((50 - map->Position[i].x) < min) {
						min = 50 - map->Position[i].x;
					}
					if (map->Position[i].y < min) {
						min = map->Position[i].y;
					}
					if ((50 - map->Position[i].y) < min) {
						min = 50 - map->Position[i].y;
					}
					//若该点可以跳出水坑 打印路径
					if (min < 10) {
						map->Position[i].distance += min;
						printf("\nSucceed:[%d][%d] dist:%d\n", map->Position[i].x, map->Position[i].y, map->Position[i].distance);
						pa = &(map->Position[i]);
						while (1) {
							if (pa->x == 24&& pa->y == 24) {
								break;
							}
							printf("<-%d-[%d][%d]", pa->distance, pa->x, pa->y);
							pa = pa->Pre;
						}
					}
				}
			}
			break;
		}
		//将顶点设为已被访问(加入S[])
		map->Position[Flag].seen = 1;
		//寻找该点所有的邻接点
		for (i = 1; i <= FOOTHOLD; i++) {
			//如果该点已被收录,则跳过该点
			if (map->Position[i].seen == 1) {
				continue;
			}
			//判断该点是否在邻接范围内
			god = ((map->Position[i].x) - (map->Position[Flag].x))*((map->Position[i].x) - (map->Position[Flag].x)) + ((map->Position[i].y) - (map->Position[Flag].y))*((map->Position[i].y) - (map->Position[Flag].y));
			god = (int)sqrt(god);
			if (god <= 10) {
				if ((god + map->Position[Flag].distance) < (map->Position[i].distance)) {
					map->Position[i].distance = god + map->Position[Flag].distance;
					map->Position[i].Pre = &(map->Position[Flag]);
				}
			}
		}
	}
}

在这里插入图片描述
程序有点长,思路基本上和上述以一致的。
最后挂个链接吧,这个链接我把程序更新了一些,原先广度搜索的程序可以实现搜索出需要跨步最少的路径。
在这里插入图片描述
链接:https://pan.baidu.com/s/1dzLrmdA0siojY55Z5S47Hw
提取码:grue

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Dijkstra算法可以用来求带权有向图上的最短路径,下面是Java实现: ```java import java.util.*; public class DijkstraAlgorithm { private static final int MAX = Integer.MAX_VALUE; // 定义无穷大 public static void dijkstra(int[][] graph, int start) { int n = graph.length; // 图的大小 int[] dist = new int[n]; // 存储起点到各个点的最短距离 boolean[] visited = new boolean[n]; // 标记各个结点是否已经访问过 int[] prev = new int[n]; // 存储到达各个结点的前驱结点 // 初始化 for (int i = 0; i < n; i++) { dist[i] = MAX; visited[i] = false; prev[i] = -1; } dist[start] = 0; // 循环n-1次,每次确定一个顶点的最短路径 for (int i = 0; i < n - 1; i++) { int minDist = MAX; int u = -1; // 找到当前未访问的结点中距离起点最近的结点 for (int j = 0; j < n; j++) { if (!visited[j] && dist[j] < minDist) { minDist = dist[j]; u = j; } } if (u == -1) { break; } visited[u] = true; // 更新与u相邻的结点的最短距离 for (int j = 0; j < n; j++) { if (!visited[j] && graph[u][j] != MAX) { int newDist = dist[u] + graph[u][j]; if (newDist < dist[j]) { dist[j] = newDist; prev[j] = u; } } } } // 输出结果 System.out.println("起点为" + start + "的最短路径如下:"); for (int i = 0; i < n; i++) { if (i != start && dist[i] != MAX) { System.out.print("从" + start + "到" + i + "的最短路径为:" + start); int j = i; while (j != start) { System.out.print(" -> " + j); j = prev[j]; } System.out.println(",路径长度为:" + dist[i]); } } } public static void main(String[] args) { int[][] graph = { {0, 1, 12, MAX, MAX}, {MAX, 0, 9, 3, MAX}, {MAX, MAX, 0, MAX, 5}, {MAX, MAX, 4, 0, 13}, {MAX, MAX, MAX, MAX, 0} }; dijkstra(graph, 0); } } ``` 运行结果: ``` 起点为0的最短路径如下: 从0到1的最短路径为:0 -> 1,路径长度为:1 从0到2的最短路径为:0 -> 1 -> 3 -> 2,路径长度为:12 从0到3的最短路径为:0 -> 1 -> 3,路径长度为:4 从0到4的最短路径为:0 -> 1 -> 3 -> 2 -> 4,路径长度为:17 ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值