【C语言】算法学习·Dijkstra算法详解

news2024/11/28 15:45:52

目录

Dijkstra算法设计

Dijkstra算法简介

Dijkstra算法的基本思想

Dijkstra贪心策略

完美图解

伪代码详解

完整代码

算法解析及优化拓展

​使用优先队列的完整代码


Dijkstra算法设计

Dijkstra算法简介

        Dijkstra算法是解决**单源最短路径**问题的**贪心算法**
        它先求出长度最短的一条路径,再参照该最短路径求出长度次短的一条路径直到求出从源点到其他各个顶点的最短路径。

Dijkstra算法的基本思想

        首先假定源点为u,顶点集合V被划分为两部分:集合 S 和 V-S。    初始时S中仅含有源点u,其中S中的顶点到源点的最短路径已经确定。
        集合S 和V-S中所包含的顶点到源点的最短路径的长度待定,称从源点出发只经过S中的点到达V-S中的点的路径为特殊路径,并用dist[]记录当前每个顶点对应的最短特殊路径长度。

Dijkstra贪心策略

        选择特殊路径长度最短的路径,将其连接的V-S中的顶点加入到集合S中,同时更新数组dist[]。一旦S包含了所有顶点,dist[]就是从源到所有其他顶点的最短路径长度。

  • (1)数据结构。 设置地图的带权邻接矩阵为map[][],即如果从源点u到顶点i有边,就令map[u][i]=<u,i>的权值,否则map[u][i]=∞;采用一维数组dist[i]来记录从源点到i顶点的最短路径长度:采用一维数组p[i]来记录最短路径上i顶点的前驱。
  • (2)初始化。令集合S={u},对于集合V-S中的所有顶点x,初始化dist[i]=map[u][i],如果源点u到顶点i有边相连,初始化p[i]=u(i的前驱是u),否则p[i]=-1
  • (3)找最小。在集合V-S中依照贪心策略来寻找使得dist[j]具有最小值的顶点t,即dist[t]=min,则顶点t就是集合V-S中距离源点u最近的顶点。
  • (4)加入S战队。将顶点t加入集合S,同时更新V-S
  • (5)判结束。如果集合V-S为空,算法结束,否则转6
  • (6)借东风。在(3)中已近找到了源点到t的最短路径,那么对集合V-S中所有与顶点t相邻的顶点j,都可以借助t走捷径。如果dist[j]>dist[t]+map[t][j],则dist[j]=dist[t]+map[t][j],记录顶点j的前驱为t,p[j]=t,转(3)。

        我自己在这里理解就是,从u找到与它最近的点t,在从t找到与它最近的点j,在....按照这样持续下去,直到最后一个点 这里我再通俗的解释下这个借东风的意思。源点为1,如果我们找到了距离源点最近的点2,且点2与3,4相连。这样,我们如果要倒3,4有两种方法:

  •                 1->2->3(4)
  •                 1->3(4)

        这里我们就要判断是从1直接到3(4)快,还是经过2后快。假设<1,2>=2 / <2,3>=3 / <1,3>=4,根据上面的数据,我们第一次找最小找到的是2结点,如果我们直接把2替换掉1当做源点继续找下一个最近的点,这种方法是错的。

        因为可以看出1->3只用4,而过2的话要用5。

完美图解

这里我就直接放图片了,书里的图不好画。但主要的是自己按照其流程过一遍,在草稿纸上自己画一遍。

在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述     

伪代码详解

跟着图解大致了解了一遍接下来就要上代码了,放心,代码不是一个完整的几十行的代码,全部按步骤划分好了的,这里方便大家粘贴。

/*
(1)数据结构
	n:城市顶点个数. m:城市间路线的条数. map[][]:地图对应的带权邻接矩阵. dist[]:记录源点u到某顶点的最短路径长度。
	p[]:记录源点到某顶点的最短路径上的该顶点的前一个顶点(前驱).flag[]:flag[i]=true说明顶点i已加入到集合S,否则该顶点属于集合V-S
*/
	const int N=100;//初始化城市个数,可修改
	const int INF=1e7;	//无穷大
	int map[N][N],dist[N],p[N],n,m;
	bool flag[N];

//(2)初始化源点u到其他各个顶点的最短路径长度,初始化源点u出边邻接点的前驱为u
	bool flag[n];//如果flag[i]=true,说明该顶点i已经加入到集合S;否则i属于集合V-S
	for(int i=1;i<=n;i++){
		dist[i]=map[u][i];	//初始化源点u到其他各个顶点的最短路径长度
		flag[i]=false;
		if(dist[i]==INF)
			p[i]=-1;	//说明源点u到顶点i无边相连,设置p[i]=-1
		else
			p[i]=u;	//说明源点u到顶点i有边相连,设置p[i]=u
	}
//(3)初始化集合S,令集合S={u},从源点u的最短路径为0
	flag[u]=true;//初始化集合S中,只有一个元素:源点u
	dist[u]=0;	//初始化源点u的最短路径为0,自己到自己的最短路径

//(4)找最小.在集合V-S中寻找距离源点u最近的顶点t,若找不到,则跳出循环;否则,将t加入集合S。
	int temp=INF,t=u;
	for(int j=1;j<=n;j++){//在集合V-S中寻找距离源点u最近的顶点t
		if(!flag[j] && dist[j]<temp){
			t=j;	//记录距离源点u最近的顶点
			temp=dist[j];
		}
	}
	if(t==u) return ;	//找不到t跳出循环
	flag[t]=true;	//否则,将t加入集合S
	
//(5)借东风。考察集合V-S中源点u到t的邻接点j的距离,如果源点u经过t到达j的路径更短,
//	则更新dist[j]=dist[t]+map[t][j],即松弛操作,并记录j的前驱为t;
	for(int j=1;j<=n;j++){//更新集合V-S中与t邻接的顶点到u的距离
		if(!flag[j] && map[t][j]<INF){//!flag[j]表示j在v-s集合中,map[t][j]<INF表示t与j邻接
			if(dist[j]>(dist[t]+map[t][j])){//经过t到达j的路径更短
				dist[j]=dist[t]+map[t][j];
				p[j]=t;	//记录j的前驱为t
			}
		}
	}	
//重复(4)~(5),知道源点u到所有顶点的最短路径被找到

完整代码

#include<bits/stdc++.h>
using namespace std;
const int N=100;	//城市个数可修改
const int INF=1e7;	//初始化无穷大为.......
int map[N][N],dist[N],p[N],n,m;	//n为城市个数,m为城市间路线的条数
bool flag[N];	//如果flag[i]=true,说明该顶点i已经加入到集合S;否则i属于集合V-S

void Dijkstra(int u){
	for(int i=1;i<=n;i++){//********>>>--1--<<<******//
		dist[i]=map[u][i];	//初始化源点u到其他各个顶点的最短路径长度
		flag[i]=false;
		if(dist[i]==INF)
			p[i]=-1;	//说明源点u到顶点i无边相连,设置p[i]=-1
		else
			p[i]=u;	//说明源点u到顶点i有边相连,设置p[i]=u
	}
	flag[u]=true;//初始化集合S中,只有一个元素:源点u
	dist[u]=0;	//初始化源点u的最短路径为0,自己到自己的最短路径
	for(int i=1;i<=n;i++){//********>>>--2--<<<******//
		int temp=INF,t=u;
		for(int j=1;j<=n;j++){//>>--3--<<在集合V-S中寻找距离源点u最近的顶点t
			if(!flag[j] && dist[j]<temp){
				t=j;	//记录距离源点u最近的顶点
				temp=dist[j];
			}
		}
		if(t==u) return ;	//找不到t跳出循环
		flag[t]=true;	//否则,将t加入集合S
		for(int j=1;j<=n;j++){//>>--4--<<更新集合V-S中与t邻接的顶点到u的距离
			if(!flag[j] && map[t][j]<INF){//!flag[j]表示j在v-s集合中,map[t][j]<INF表示t与j邻接
				if(dist[j]>(dist[t]+map[t][j])){//经过t到达j的路径更短
					dist[j]=dist[t]+map[t][j];
					p[j]=t;	//记录j的前驱为t
				}
			}
		}	
	}	
}

int main(){
		int u, v, w, st;
	system("color 0d");
	cout << "请输入城市的个数:" << endl;
	cin >> n;
	cout << "请输入城市之间的路线个数" << endl;
	cin >> m;
	cout << "请输入城市之间的路线以及距离" << endl;
	for(int i=1;i<=n;i++)//初始化图的邻接矩阵
		for (int j = 1; j <= n; j++)
		{
			map[i][j] = INF;//初始化邻接矩阵为无穷大
		}
	while (m--)
	{
		cin >> u >> v >> w;
		map[u][v] = min(map[u][v], w);	//邻接矩阵存储,保留最小的距离
	}
	cout << "请输入小明所在的位置:" << endl;
	cin >> st;
	Dijkstra(st);
	cout << "小明所在的位置:" << st << endl;
	for (int i = 1; i <= n; i++)
	{
		cout << "小明:" << st << " - " << "要去的位置:" << i << endl;
		if (dist[i] == INF)
			cout << "sorry,无路可达" << endl;
		else
			cout << "最短距离为:" << dist[i] << endl; 
	}
	return 0;
}
输入
请输入城市的个数:
5
请输入城市之间的路线个数
11
请输入城市之间的路线以及距离
1 5 2
5 1 8
1 2 16
2 1 29
5 2 32
2 4 13
4 2 27
1 3 15
3 1 21
3 4 7
4 3 19
请输入小明所在的位置:
5

输出
小明所在的位置:5
小明:5 - 要去的位置:1 最短距离为:8
小明:5 - 要去的位置:2 最短距离为:24
小明:5 - 要去的位置:3 最短距离为:23
小明:5 - 要去的位置:4 最短距离为:30
小明:5 - 要去的位置:5 最短距离为:0
因为我们在程序中使用了p[]数组记录了最短路径上每一个结点的前驱,所以我们可以增加一段程序逆向该最短路径上的城市序列。
void findpath(int u)
{
	int x;
	stack<int>s;
	cout << "源点为:" << u << endl;
	for (int i = 1; i <= n; i++)
	{
		x = p[i];
		while (x != -1)
		{
			s.push(x);
			x = p[x];
		}
		cout << "源点到其他各顶点的最短路径为:";
		while (!s.empty())
		{
			cout << s.top() << "--";
			s.pop();
		}
		cout << i << ";最短距离为:" << dist[i] << endl;
	}
}
只需要在主函数末尾调用即可

结果为:
源点为:5
源点到其他各顶点的最短路径为:5--1;最短距离为:8
源点到其他各顶点的最短路径为:5--1--2;最短距离为:24
源点到其他各顶点的最短路径为:5--1--3;最短距离为:23
源点到其他各顶点的最短路径为:5--1--3--4;最短距离为:30
源点到其他各顶点的最短路径为:5;最短距离为:0

算法解析及优化拓展

在这里插入图片描述
在这里插入图片描述

使用优先队列的完整代码

#include<queue>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 100;//城市的个数可修改
const int INF = 1e7;//初始化无穷大为10000000
int map[N][N], dist[N], p[N], n, m;//n为城市的个数,m为城市间路线的条数
int flag[N];	//	如果flag[i]==true,说明顶点i已经加入到集合S;否则顶点i属于集合V-S

struct Node {
	int u, step;
	Node() {};
	Node(int a, int sp)
	{
		u = a, step = sp;
	}
	bool operator<(const Node& a)const {//重载 <
		return step > a.step;
	}
};

void Dijkstra(int st)
{
	priority_queue<Node>Q;//优先队列优化
	Q.push(Node(st, 0));
	memset(flag, 0, sizeof(flag));//初始化flag数组为0
	for (int i = 1; i <= n; ++i)
		dist[i] = INF;//初始化所有距离为无穷大
	dist[st] = 0;
	while (!Q.empty())
	{
		Node it = Q.top();//优先队列列头元素为最小值
		Q.pop();
		int t = it.u;
		if (flag[t])//说明已经找到了最短距离,该节点是队列里面的重复元素
			continue;
		flag[t] = 1;
		for (int i = 1; i <= n; i++)
		{
			if(!flag[i] && map[t][i]<INF)//判断与当前点有关系的点,并且自己不能到自己
				if (dist[i] > dist[t] + map[t][i])
				{
					//求距离当前点的每个点的最短距离,进行松弛操作
					dist[i] = dist[t] + map[t][i];
					Q.push(Node(i, dist[i]));//把更新后的最短距离压入队列中,注意:里面有重复元素
				}
		}
	}

}

int main()
{
	int u, v, w, st;
	system("color 0d");
	cout << "请输入城市的个数:" << endl;
	cin >> n;
	cout << "请输入城市之间的路线个数" << endl;
	cin >> m;
	cout << "请输入城市之间的路线以及距离" << endl;
	for (int i = 1; i <= n; i++)//初始化图的邻接矩阵
		for (int j = 1; j <= n; j++)
		{
			map[i][j] = INF;//初始化邻接矩阵为无穷大
		}
	while (m--)
	{
		cin >> u >> v >> w;
		map[u][v] = min(map[u][v], w);	//邻接矩阵存储,保留最小的距离
	}
	cout << "请输入小明所在的位置:" << endl;
	cin >> st;
	Dijkstra(st);
	cout << "小明所在的位置:" << st << endl;
	for (int i = 1; i <= n; i++)
	{
		cout << "小明:" << st << " ---> " << "要去的位置:" << i;
		if (dist[i] == INF)
			cout << "sorry,无路可达" << endl;
		else
			cout << " 最短距离为:" << dist[i] << endl;
	}
	return 0;
}

/*
请输入城市的个数:
5
请输入城市之间的路线个数
11
请输入城市之间的路线以及距离
1 5 2
5 1 8
1 2 16
2 1 29
5 2 32
2 4 13
4 2 27
1 3 15
3 1 21
3 4 7
4 3 19
请输入小明所在的位置:
5
小明所在的位置:5
小明:5 ---> 要去的位置:1 最短距离为:8
小明:5 ---> 要去的位置:2 最短距离为:24
小明:5 ---> 要去的位置:3 最短距离为:23
小明:5 ---> 要去的位置:4 最短距离为:30
小明:5 ---> 要去的位置:5 最短距离为:0
*/

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/626643.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2021年国赛高教杯数学建模C题生产企业原材料的订购与运输解题全过程文档及程序

2021年国赛高教杯数学建模 C题 生产企业原材料的订购与运输 原题再现 某建筑和装饰板材的生产企业所用原材料主要是木质纤维和其他植物素纤维材料,总体可分为 A&#xff0c;B&#xff0c;C 三种类型。该企业每年按 48 周安排生产&#xff0c;需要提前制定 24 周的原材料订购和…

如何在 javascript 中按属性值查找数组中的对象

文章目录 使用 find() 方法按属性值在数组中查找对象使用 filter() 方法按属性值查找数组中的对象使用 JavaScript for 循环按属性值查找数组中的对象使用 JavaScript for...in 循环按属性值查找数组中的对象 数组指的是值的有序列表&#xff0c;每个值称为由索引指定的元素。 …

这所西安的985专硕爆冷,保护一志愿,过线即上岸!

本期为大家整理热门院校“西北工业大学”的择校分析&#xff0c;这个择校分析专题会为大家结合&#xff1a;初试复试占比、复试录取规则&#xff08;是否公平&#xff09;、往年录取录取名单、招生人数、分数线、专业课难度等进行分析。希望能够帮到大家! –所有数据来源于研招…

Plot、Scatter、Subplot函数用法

目 录 一、Plot()函数 二、Scatter()函数 三、Subplot()函数 一、Plot()函数 格式&#xff1a;matplotlib.pyplot.plot(x,y,format_string.**kwargs) 说明&#xff1a; x:x轴数据&#xff0c;列表或数组&#xff0c;可选(注&#xff1a;当绘制多条曲线时&#xff0c;不能省…

IT 系统巡检必须关注的指标总结

1. 系统整体架构 以下内容作为基本 IT 系统信息被首先调查记录&#xff0c;供分析参考使用。 ● 网络设备配置 ---设备型号, IOS 版本, 模块型号和数量,用途 ● 存储系统配置 ---设备型号, IO 带宽, Cache 容量&#xff0c;磁盘数量&#xff0c;接入模式&#xff0c;存储容…

Go项目配置管理工具---Viper

目录 Viper概述前言功能viper配置优先级 从Viper中获取值读取配置文件注册和使用别名 把值写入Viper设置默认值使用Set方法设置值把配置信息写入配置文件从io.Reader中读取配置信息到viper 监控Viper文件 Viper概述 前言 对于现代应用程序&#xff0c;尤其大中型的项目来说&a…

【MySQL】Mycat

文章目录 什么是Mycat为什么要用Mycatmycat能干什么各数据库中间件对比Mycat原理数据库中间件逻辑库逻辑表分片表分片规则全局表ER表非分片表分片节点节点主机mycat安装mycat核心配置schema.xmlserver.xmlrule.xml加密明文密码&#xff08;可选&#xff09; MyCat读写分离垂直拆…

OpenCV中的图像处理3.11(10) OpenCV中的图像变换

目录 3.11 OpenCV中的图像变换3.11.1 傅里叶变换目标理论Numpy中的傅里叶变换OpenCV中的傅立叶变换DFT的性能优化为什么Laplacian是一个高通滤波器&#xff1f;其他资源 翻译及二次校对&#xff1a;cvtutorials.com 编辑者&#xff1a;廿瓶鲸&#xff08;和鲸社区Siby团队成员&…

2.3 YARN伪分布式集群搭建

任务目的 重点掌握 YARN 集群的相关配置学会启动和关闭 YARN 集群的两种方式能够使用 jps 命令查看进程的启动情况能够通过 UI 查看 YARN 集群的运行状态任务清单 任务1:YARN 集群主要配置文件讲解任务2:YARN 集群测试任务步骤 任务1:YARN 集群主要配置文件讲解 1.1 配置环…

基于多尺度图神经网络的流场预测,实现精度与速度的平衡

项目简介 本项目来源于飞桨AI for Science共创计划的论文复现赛题&#xff0c;复现论文为《AMGNET: multi-scale graph neural networks for flow field prediction》。该论文主要采用图神经网络&#xff0c;因为在计算流体力学中计算域被网格离散化&#xff0c;这与图结构天然…

将PDF1页分割为4页

运行效果 原始PDF 分割后PDF 一、python代码&#xff08;用的是python3.9.0版本&#xff09; import os import tempfile from pdf2image import convert_from_path from PIL import Image from PyPDF2 import PdfReader, PdfWriterdef split_pdf_page(pdf_path, output_path…

4G开发板-安卓手机开发套件-MTK主板开发板定制

开发板是一种用于嵌入式系统开发的电路板&#xff0c;它包含了各种硬件组件&#xff0c;如中央处理器、存储器、输入设备、输出设备、数据通路/总线以及外部资源接口等。为了满足特定的开发需求&#xff0c;嵌入式系统开发者通常会根据项目要求来定制开发板&#xff0c;当然用户…

2023年前端面试高频考点ES6

目录 ES6新增 数据类型 基本数据类型 引用数据类型 Null&#xff0c;NaN&#xff0c;Undefined toString&#xff0c;valueOf &#xff0c;&#xff0c;Object.is() 判断数据类型&#xff1a;typeof运算符&#xff0c;instance of运算符&#xff0c;isPrototypeOf() 方…

数据分析第11课pandas时间序列(上+下)-第12期15,16课

数据分析思维:有逻辑性, 课前练习1 各大平台相继推出和迭代付费会员策略,如优酷会员、京东PLUS会员、网易云音乐黑胶VIP等,通过提供丰富的权益吸引用户成为付 费会员,提升用户黏性和忠诚度。请围绕付费会员回答以下问题: 以网易云音乐黑胶VIP为例,运营同学希望推出活动…

k8s 使用helm安装longhorn存储控制器

1.安装helm 参考&#xff1a;k8s helm安装使用_Apex Predator的博客-CSDN博客 2.配置基础环境 安装longhorn存储控制器需要用到iSCSI工具&#xff0c;所以需要在k8s所有节点上安装 yum -y install iscsi-initiator-utils 3.安装longhorn 3.1配置helm镜像源 helm repo ad…

kafka Ar 、ISR 、 OSR 已分配副本 同步中副本 、不同步副本 Controller 执行leader 重新分配

目录 Ar 、ISR 、 OSR 已分配副本 同步中副本 、不同步副本 Controller 执行leader 重新分配 Ar 、ISR 、 OSR 已分配副本 同步中副本 、不同步副本 AR分区所有已分配副本 ISR 在同步中的副本OSR 不同步副本 如果有一个节点挂掉&#xff0c;分区领导会渠道其他地方当上领导…

Git的理解以及在IDEA中的使用

什么是版本控制 版本控制是指记录一段时间内对一个文件或一组文件的更改的系统&#xff0c;称为“版本”。换句话说&#xff0c;这些版本将帮助您跟踪代码/项目中的更改&#xff0c;如果需要&#xff0c;还可以撤消这些更改。 当处理较大的项目时&#xff0c;这种能够比较、区…

“RAID0 vs RAID1 vs RAID5 vs RAID6 vs RAID10:哪种RAID级别最适合你的需求?“

概要&#xff1a; RAID&#xff08;Redundant Array of Independent Disks&#xff09;是一种数据存储技术&#xff0c;可以将多个硬盘组合起来以提高性能、可靠性和容错能力。下面是几种常见的RAID级别&#xff0c;以及它们的用途和特点。 目录 RAID 0RAID 1RAID 5RAID 6RAID…

navicat 怎么导入运行bak文件,详细教程

文章目录 一、新建数据库&#xff0c;点击高级设置访问权限二、点击SQL Server备份三、右击空白&#xff0c;点击从文件还原四、找到备份的bak文件五、 点击“高级”选项六、然后点击 生产SQL&#xff0c;最后点 还原 一、新建数据库&#xff0c;点击高级设置访问权限 二、点击…

Opencv-python 将图片中某段HSV范围内的所有像素去除,用白色替换

文章目录 前言一、效果二、代码1.思路2.代码解释 总结 前言 本次实验要求将模拟飞行座舱图像中的HUD绿色字体去掉&#xff0c;并且用白色来替代&#xff0c;最终输出没有绿色字体的图片&#xff0c;用于下一步的某种图像算法的输入。 一、效果 二、代码 1.思路 将一张图片中的…