简单路径规划算法

简单路径规划算法

1 Floyd - Warshall

①动态规划
②可以处理负边权的图,不可处理负权环的图

1.1 C语言实现

#include<stdio.h>

int main()
{
	int e[10][10], k, i, j, n, m, t1, t2, t3;
	int inf = 99999999; //正无穷值 
	
	printf("%s","请输入:顶点数 边数\n");	
	scanf("%d %d", &n, &m);
	
 	//初始化 
	for (i = 1; i <= n; i++)
		for (j = 1; j <= n; j++)
			if (i == j) e[i][j] = 0;
				else e[i][j] = inf;
				
	//读入边
	printf("%s","请输入:起点 终点 权值\n");
	for (i = 1; i <= m; i++)
	{
		scanf("%d %d %d",&t1,&t2,&t3);
		e[t1][t2] = t3;
	}

	//Floyd-Warshall算法核心语句 	
	for (k = 1; k <= n; k++) //一个个遍历每个点做中转点
		for (i = 1; i <= n; i++)
			for (j = 1; j <= n; j++)
				if (e[i][k]<inf && e[k][j]<inf && e[i][j] > e[i][k] + e[k][j]) //不允许正无穷相加
					e[i][j] = e[i][k] + e[k][j];
	
	//输出 
	printf("%s","任意两点最短路程:\n");	
	for (i = 1; i <= n; i++)
	{
		for (j = 1; j <= n; j++)
		{
			printf("%10d", e[i][j]);
		}
		printf("\n");
	}
	return 0;
}

2 Dijkstra(狄克斯特拉)

2.1 C语言实现

①贪心算法
②单源最短路径问题,实用于有向 无环 正权图
③思想:每次找到离源点最近的一个顶点,然后以该点为中心进行拓展松弛,最终得到源点到所有点的最短路径
④举例:若离1点是起点,最近的点是2,肯定不可能通过第三个点中转,使1号到2号路程进一步缩短。因为1号顶点到其他顶点路程 肯定没有 1号到2号点短

#include<stdio.h>

int main()
{
	int e[10][10], dis[10], book[10], i, j, n, m, t1, t2, t3, u, v, min;
	int inf = 99999999; //正无穷值 

	printf("%s", "请输入:顶点数 边数\n");
	scanf("%d %d", &n, &m);

	//初始化 
	for (i = 1; i <= n; i++)
		for (j = 1; j <= n; j++)
			if (i == j) e[i][j] = 0;
			else e[i][j] = inf;

			//读入边
			printf("%s", "请输入:起点 终点 权值\n");
			for (i = 1; i <= m; i++)
			{
				scanf("%d %d %d", &t1, &t2, &t3);
				e[t1][t2] = t3;
			}

			//初始化dis数组,这里是1号顶点到其余各顶点的初始路程
			for (i = 1; i <= n; i++)
				dis[i] = e[1][i];

			//book数组初始化
			//book数组记录已知最短路程的顶点
			for (i = 1; i <= n; i++)
				book[i] = 0;
			book[1] = 1;

			//dijkstra 狄克斯特拉 算法核心语句 	
			//松弛 
			for (i = 1; i <= n - 1; i++)
			{
				//找到离1号顶点最近的顶点
				min = inf;
				for (j = 1; j <= n; j++)
				{
					if (book[j] == 0 && dis[j] < min)
					{
						min = dis[j];
						u = j;
					}
				}
				book[u] = 1;
				//对u顶点所有出边进行松弛 
				for (v = 1; v <= n; v++)
				{
					if (e[u][v] < inf)
					{
						if (dis[v] > dis[u] + e[u][v])
							dis[v] = dis[u] + e[u][v];
					}
				}
			}

			//输出 
			printf("%s", "第一点到各点的最短路程:\n");
			for (i = 1; i <= n; i++)
				printf("%d ", dis[i]);
				
			getchar();
			getchar();
			return 0;
}

3 Bellman-Ford

3.1 C语言实现

①单源最短路径问题
②思想:对所有边进行n-1次松弛操作
②1号顶点只能通过一条边到达其余顶点(第1轮对所有边进行松弛) -> 1号顶点只能通过二条边到达其余顶点(第2轮对所有边进行松弛) -> … -> 1号顶点只能通过k条边到达其余顶点(第k轮对所有边进行松弛)。最多n-1轮
③每一轮松弛,有些顶点已经确定是最短路,此后值会一直不变,不再受后续松弛影响,但每次还需判断是否松弛,优化见Bellman-Ford的队列优化

#include<stdio.h>

int main()
{
	int dis[10], bak[10], i, k, n, m, u[10], v[10], w[10], check, flag;
	int inf = 99999999; //正无穷值 

	printf("%s", "请输入:顶点数 边数\n");
	scanf("%d %d", &n, &m);

	//读入边
	printf("%s", "请输入:起点 终点 权值\n");
	for (i = 1; i <= m; i++)
		scanf("%d %d %d", &u[i], &v[i], &w[i]);
	
	//初始化dis数组,这里是1号顶点到其余各顶点的初始路程
	for (i = 1; i <= n; i++)
		dis[i] = inf;
	dis[1] = 0;

	//Bellman-Ford算法核心语句 	
	for (k = 1; k <= n - 1; k++)
	{
		check = 0; //用来标记在本轮松弛中数组dis是否会发生更新
		//进行一轮松弛
		for (i = 1; i <= m; i++)
		{
			if (dis[v[i]] > dis[u[i]] + w[i])
			{
				dis[v[i]] = dis[u[i]] + w[i];
				check = 1; //若数组dis发生变化,改变check的值
			}
		}
		//松弛完毕后检测数组dis是否有更新
		if (check == 0) break;	//如果数组dis没有更新,提前退出循环结束算法
								//每一轮是对所有边进行松弛,所以不一定判n-1轮
	}
		//检测负权回路
		flag = 0;
		for (i = 1; i <= m; i++)
			if (dis[v[i]] > dis[u[i]] + w[i]) flag = 1;

		if(flag ==1) printf("此图含有负权回路");
		else
		{
			//输出最终结果
			for (i = 1; i <= n; i++)
				printf("%d ", dis[i]);
		}
		
		getchar();
		getchar();
		return 0;
}

4 Bellman-Ford的队列优化

4.1 C语言实现

①只有在前一遍松弛中改变了最短路程估计值的顶点,才可能引起它们邻接点最短路程估计值发生改变。因此用一个队列来存放被成功松弛的顶点,之后只对队列中的点的出边进行松弛。
②临接表的数组表示:first[u[i]]保存顶点u[i]的第一条编号,next[i]存储“编号为i的边”的下一条边的编号。
③数组模拟邻接表可见:https://blog.csdn.net/qq_17550379/article/details/94871801

#include<stdio.h>

int main()
{
	int n, m, i, j, k, flag;
	//u,v,w的数组大小要根据实际情况来设置,要比m的最大值要大1
	int u[8], v[8], w[8];
	//first要比n的最大值要大1,next要比m的最大值要大1
	int first[6], next[8];
	//book数组用来记录哪些顶点已经在队列中
	int dis[6] = { 0 }, book[6] = { 0 };
	//定义一个队列,并初始化队列
	int que[101] = { 0 }, head = 1, tail = 1;
	int inf = 99999999; //正无穷值 

	printf("%s", "请输入:顶点数 边数\n");
	scanf("%d %d", &n, &m);
	
	//初始化dis数组,这里是1号顶点到其余各个顶点的初始路程
	for (i = 1; i <= n; i++)
		dis[i] = inf;
	dis[1] = 0;

	//初始化book数组,初始化为0,刚开始都不在队列中
	for (i = 1; i <= n; i++) book[i] = 0;

	//初始化first数组下标1~n的值为-1,表示1~n顶点暂时都没有边
	for (i = 1; i <= n; i++) first[i] = -1;
	
	//读入边
	printf("%s", "请输入:起点 终点 权值\n");
	for (i = 1; i <= m; i++)
	{
		scanf("%d %d %d", &u[i], &v[i], &w[i]);
		//建立临接表的关键
		next[i] = first[u[i]];
		first[u[i]] = i;
	}

	//1号顶点入队
	que[tail] = 1;
	tail++;
	book[1] = 1; //标记1号顶点已经入队

	while (head < tail) //队列不为空的时候循环
	{
		k = first[que[head]]; //当前需要处理的队首顶点
		while (k != -1) //扫描当前顶点的所有出边
		{
			if (dis[v[k]] > dis[u[k]] + w[k]) //判断是否松弛成功
			{
				dis[v[k]] = dis[u[k]] + w[k]; //更新顶点1到顶点v[k]的路程
				//book用来判断顶点v[k]是否在队列中
				if (book[v[k]] == 0) //表示不在队列中,将顶点v[k]加入队列中
				{
					//入队
					que[tail] = v[k];
					tail++;
					book[v[k]] = 1; //同时标记顶点v[k]已经入队
				}
			}
			k = next[k];
		}
		//出队
		book[que[head]] = 0;
		head++;
	}
		
		//判断负权回路
		flag = 0;
		for (i = 1; i <= m; i++)
			if (dis[v[i]] > dis[u[i]] + w[i]) flag = 1;

		if(flag ==1) printf("此图含有负权回路"); 
		else
		{
			//输出最终结果
			for (i = 1; i <= n; i++)
				printf("%d ", dis[i]);
		}
		getchar();
		getchar();
		return 0;
}

5 小结

FloydDijkstraBellman-Ford队列优化的Bellman-Ford
空间复杂度O(N^2)O(M)O(M)O(M)
时间复杂度O(N^3)O((M+N)logN))O(NM)O(NM)
适用情况稠密图和顶点关系密切稠密图和顶点关系密切稀疏图和边关系密切稀疏图和边关系密切
负权可以解决不可解决可以解决可以解决
所求任两点间最短路单源最短路径问题单源最短路径问题单源最短路径问题
判定是否存在负权回路不能不能可以可以
思路三层循环枚举判断是否会有更短路从所求点更新周围所有点然后每次走到最近的点对所有边进行n-1次松弛操作更新dis与未优化差不多,只是用了队列
  • 4
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值