【算法与数据结构】Dijkstra算法求单源最短路径问题
目录
Dijkstra算法
算法简介:
该算法的核心思想:
算法特点:
算法示例演示:
算法实现:
邻接矩阵存图
邻接表存图:
时间复杂度分析:
Dijkstra算法
算法简介:
Dijkstra算法,是用来算出图中一个顶点到其余各顶点的最短路径。适合于解决带权的有向图中的单源最短路径问题。
该算法的核心思想:
- 声明一个dist数组来保存源点src到各个顶点的最短距离。dist[i]表示从顶点src到顶点i的最短距离。初始化时为无穷大。
- 采用贪心算法的思想,每次遍历距离始点最近且未被访问过的邻接节点。然后更新dist数组,直到扩展到终点。
算法特点:
- 该算法使用了广度优先搜索解决赋权有向图或无向图的单源最短路径。
- Dijkstra算法要求图中所有边的权重非负,否则可能得到错误结果。
算法示例演示:

这里用邻接矩阵来存图, 邻接矩阵是一个二维数组,其中matrix[i][j]表示节点i到节点j的权重。
我们还需记录哪些节点已经访问过了,也就是哪些节点已经确定最短距离了。我们可以用一个bool类型的一维数组来记录。vector<bool> visited; visited[i]=false表示顶点i还没有访问过。
初始化:

开始更新,遍历每个节点,首先遍历节点v1。
- 先找出距离最小的顶点,也就是dist数组中的最小值,顶点v1。此时将visited[1]置为true,该顶点的最小路径已经确定。
- 这个顶点v1有出度,访问它的邻接节点,有v3,v5,v6三个节点。v1->v3=10,v1->v5=30,v1->v6=100。发现它们都比dist[3],dist[5],dist[6]要小,那么更新他们。

- 再次找出距离最小的顶点,去除顶点v1,因为v1的最短路径已经确定。发现是顶点v3,那么就可以更新dist[3]=10。此时v1到v3的距离就已经确定了,将visited[3]=true。
为什么?因为图中边权都是正数 ,目前离v1顶点最近的是顶点v3,那么肯定不可能通过第三个顶点中转,使得v1到v3的距离缩短,因为我们在开始选顶点的时候,v1到v3是最小的,也就是v1顶点到其他顶点的距离肯定没有v1到v3短。
- 现在已经确定了一个顶点的最短路径,这个顶点v3有出度,去找它的邻接节点,发现有顶点v4,v1->v4的距离存储在dist[4]中,为INT_MAX,而v1->v3->v4=10+50=60,所以更新dist[4]为60。此时不能将 visited[4]置为true,因为这是我们通过v1->v3更新的v4,也有可能有其他更短的路径。
这一步可以总结为v1到v3的路程,即dist[3],通过边v3->v4松弛成功,得到v1到v4的距离,这一步叫做松弛更新。通过“边”来松弛v1顶点到其余各个顶点的路程。这是该算法的核心。

再次找出距离最小的顶点,除去顶点v1和顶点v3,因为这两个顶点已经确定最短路径。
发现dist[5]=30是最小的,和上面的解释原理相似,此时我们就可以确定出顶点v1到顶点v5的最短距离为30,vistied[5]置为true。
然后考虑v5的出度,也就是v5的邻接节点,我们发现v1->v5->v4的距离为30+20=50,小于dist[4]=60,那么更新dist[4]=50。还有v1->v5->v6=90,小于dist[6]=100,所以也更新dist[6]=100; //松弛更新

然后再次找出距离最小的顶点,去除顶点v1,v3,v5。 发现是v4,那么visited[4]置为true,表示顶点4已经确定最短距离。
然后是考虑顶点4的出度,发现v1->v4->v6(v1->v5->v4->v6)=60,小于dist[6],所以更新dist[6]=60。

最后再找距离最小的顶点,发现没有,说明所有顶点已经处理。最后的结果存于dist数组中,dist[i]就记录顶点v1到顶点i的最短路径,如果dist[i]=INT_MAX,说明顶点v1不可达顶点i。
算法结果:
v1到v1的最短距离:0
v1到v2的最短距离:不可达
v1到v3的最短距离:10
v1到v4的最短距离:50
v1到v5的最短距离:30
v1到v6的最短距离:60
算法实现:
对以上过程总结如下:
初始化:
dist数组记录起点到各节点的最短距离,初始时除起点外均为INF。
visited数组标记节点是否已确定最短路径。循环处理所有节点:
选择未访问的最小距离节点:遍历所有节点,找到当前距离最小的未访问节点
u。标记
u为已访问:此时u的最短路径已确定。松弛操作:遍历所有节点
v,若存在边u→v且通过u的路径更短,则更新dist[v]。终止条件:所有节点均被访问或剩余节点不可达。
这里会使用两种结构:邻接表和邻接矩阵。其中邻接表在稠密图中更高效,而邻接矩阵适合稀疏图。
邻接矩阵存图

以上图中的有向图为例。 (假设v1为源点)
//邻接矩阵存图
//.......
#include <iostream>
#include <vector>
using namespace std;const int INF = INT_MAX;
vector<int> dist;//记录最短路径
vector<int> parent;//记录父路径
void dijkstra(vector<vector<int>>& graph, int start_node)
{int n = graph.size();dist.resize(n, INF);parent.resize(n, -1);dist[start_node] = 0;vector<bool> visited(n, false);for (int i = 0; i < n; i++){//找到未访问节点中的距离最短的节点int u = -1;int min_dist = INF;for (int j = 0; j < n; j++){if (!visited[j] && dist[j] < min_dist){min_dist = dist[j];u = j;}}if (u == -1) break; //所有节点已处理visited[u] = true;//松弛更新//u->v 更新u所有邻接节点的距离for (int v = 0; v < n; v++){if (!visited[v] && graph[u][v] != INF) //可达并且未处理过{if (dist[v] > dist[u] + graph[u][v]){//更新dist[v] = dist[u] + graph[u][v];parent[v] = u;}}}}
}
int main()
{//邻接矩阵表示图(INF表示不可达)vector<vector<int>> matrix = {{INF,INF,10,INF,30,100},{INF,INF,5,INF,INF,INF},{INF,INF,INF,50,INF,INF},{INF,INF,INF,INF,INF,10},{INF,INF,INF,20,INF,60},{INF,INF,INF,INF,INF,INF}};int n = matrix.size();int start_node = 0;dijkstra(matrix, start_node);// 输出结果cout << "从节点 " << start_node << " 到各节点的最短距离:" << endl;for (int i = 0; i < n; ++i) {if (dist[i] == INF)cout << "节点 " << i << ": 不可达" << endl;elsecout << "节点 " << i << ": " << dist[i] << endl;}//打印路径for (int i = 0; i < n; i++){if (i != start_node){vector<int> path;//逆序存储当前顶点的路径int parenti = i;while (parenti != -1){path.push_back(parenti);parenti = parent[parenti];}reverse(path.begin(), path.end());for (auto j : path){cout << j << "->";}cout << dist[i] << endl;}}return 0;
}

数组下标从0开始,所以与上面的演示结果编号相差1。
邻接表存图:
思路是一样的,只是这里我们可以使用优先级队列快速找到距离最短的边。
- 在邻接表的实现中,使用优先队列来处理节点,每次取出距离最小的节点,然后遍历其邻接表。
- 而在邻接矩阵中,每次处理节点u时,需要遍历所有可能的v,检查是否有边,并更新距离。
具体结构:
每个节点有一个vector存储相邻节点和对应的边权。使用pair<int,int>存储。
/ 定义图的邻接表结构:pair<相邻节点, 边权重> typedef pair<int, int> Edge; typedef vector<vector<Edge>> Graph;Graph graph = {{{1, 4}, {2, 1}}, // 节点0的边:0→1(权4)、0→2(权1){{3, 2}}, // 节点1的边:1→3(权2){{1, 2}, {3, 5}}, // 节点2的边:2→1(权2)、2→3(权5){{4, 3}}, // 节点3的边:3→4(权3){} // 节点4的边:无};
然后,创建距离数组dist,初始化为无穷大,起点的距离设为0。
- 这里在找当前最短距离的节点时,为了快速找到距离最短的节点,我们可以使用优先级队列存储边,建小堆。C++的优先队列默认是最大堆,所以需要使用greater来转为最小堆。
- 在处理每个节点时,需要检查当前距离是否已经是最短,如果已经存在更短的路径,就跳过。否则,遍历所有邻接节点,更新它们的距离,并将新距离加入队列。
- 需要注意的是,可能有多个相同节点以不同距离存在于队列中,但一旦处理过更短的距离,后续的就可以忽略。因此,在取出节点时,需要检查当前记录的距离是否小于队列中的距离,如果是,就跳过。
//邻接表存图
//......
#include <iostream>
#include <vector>
#include <queue>
using namespace std;//pair<相邻节点, 边权重>
typedef pair<int, int> Edge;
typedef vector<vector<pair<int, int>>> Graph;
vector<int> dijkstra(const Graph& graph, int start)
{int n = graph.size();vector<int> dist(n, INT_MAX); // 存储起点到各节点的最短距离priority_queue<Edge, vector<Edge>, greater<Edge>> pq; // 小顶堆:按距离排序dist[start] = 0;pq.push({ 0,start });//初始节点入队while (!pq.empty()){int u = pq.top().second; // 当前距离最小的节点int curr_dist = pq.top().first;pq.pop();// 如果当前路径不是最短的,跳过处理if (curr_dist > dist[u]) continue;// 遍历u的所有邻接节点vfor (const Edge& edge : graph[u]) {int v = edge.first; // 邻接节点int weight = edge.second; // 边权重// 如果通过u到v的路径更短,则更新距离if (dist[v] > dist[u] + weight){dist[v] = dist[u] + weight;pq.push({ dist[v], v }); // 将新距离加入队列}}}return dist;
}
int main()
{// 图的邻接表表示(有向图)Graph graph = {{{1, 4}, {2, 1}}, // 节点0的边:0→1(权4)、0→2(权1){{3, 2}}, // 节点1的边:1→3(权2){{1, 2}, {3, 5}}, // 节点2的边:2→1(权2)、2→3(权5){{4, 3}}, // 节点3的边:3→4(权3){} // 节点4的边:无};int start_node = 0;vector<int> dist= dijkstra(graph, start_node);// 输出结果cout << "从节点 " << start_node << " 到各节点的最短距离:" << endl;for (int i = 0; i < dist.size(); ++i) {if (dist[i] == INT_MAX)cout << "节点 " << i << ": 不可达" << endl;elsecout << "节点 " << i << ": " << dist[i] << endl;}return 0;
}
时间复杂度分析:
V是顶点数,E是边数。
邻接矩阵实现的时间复杂度是O(V^2),而使用优先队列的邻接表实现是O((V+E)logV)。因此,在稀疏图中,邻接表的效率更高,但邻接矩阵可能在代码上更简单,尤其是对于稠密图。
相关文章:
【算法与数据结构】Dijkstra算法求单源最短路径问题
目录 Dijkstra算法 算法简介: 该算法的核心思想: 算法特点: 算法示例演示: 算法实现: 邻接矩阵存图 邻接表存图: 时间复杂度分析: Dijkstra算法 算法简介: Dijkstra算法&am…...
.CSV file input into contact of outlook with gibberish. .csv文件导入outlook, 出现乱码
workaround : 清理excel或者csv文件的格式, 使用手动先输入几个常规字, 然后使用格式刷...
StableDiffusion打包 项目迁移 项目分发 0
StableDiffusion项目迁移 0 先看了几个其他人的本地部署文章和视频,对别人的步骤做记录。(写的很潦草,只是注意一下有什么点需要注意) 虽然秋叶大佬有整合包,但是我是为了项目分发学习的,还是想自己配环境…...
关于Postman自动获取token
在使用postman测试联调接口时,可能每个接口都需要使用此接口生成的令牌做Authorization的Bearer Token验证,最直接的办法可能会是一步一步的点击,如下图: 在Authorization中去选择Bearer Token,然后将获取到的token粘贴…...
LSTM长短期记忆网络-原理分析
1 简介 概念 LSTM(Long Short-Term Memory)也称为长短期记忆网络,是一种改进的循环神经网络(RNN),专门设计用于解决传统RNN的梯度消失问题和长程依赖问题。LSTM通过引入门机制和细胞状态,能够更…...
sql server笔记
创建数据库 use master gocreate database stuuuuu//删除数据库if db_id ($$$) is not nullDrop database [$$$] go//新建表USE [studyTest] GOSET ANSI_NULLS ON GOSET QUOTED_IDENTIFIER ON GOCREATE TABLE [dbo].[Table_1]([id] [int] NULL,[name] [varchar](10) NULL ) ON…...
AI Video Composer:基于Qwen2.5-Coder的简易开源视频创作利器
系列篇章💥 No.文章1短视频开源项目MoneyPrinterTurbo:AI副业搞起来,视频制作更轻松!2【FunClip】阿里开源AI视频剪辑神器:全面体验与教程3Tailor:免费开源 AI 视频神器,创作者必备利器4Clappe…...
AI数字人开发,引领科技新潮流
引言 随着人工智能技术的迅猛发展,AI 数字人在影视娱乐、客户服务、教育及医疗等多个领域展现出巨大的潜力。本文旨在为开发者提供一份详细的 AI 数字人系统开发指南,涵盖从基础架构到实现细节的各个方面,包括人物建模、动作生成、语音交互、…...
VoIP之音频3A技术
音频3A技术是改善语音通话质量的三种关键技术的简称,包括声学回声消除(Acoustic Echo Cancellation, AEC)、自动增益控制(Automatic Gain Control, AGC)、自噪声抑制(Automatic Noise Suppression, ANS&…...
[原创]openwebui解决searxng通过接口请求不成功问题
openwebui 对接 searxng 时 无法查询到联网信息,使用bing搜索,每次返回json是正常的 神秘代码: http://172.30.254.200:8080/search?q北京市天气&formatjson&languagezh&time_range&safesearch0&languagezh&locale…...
Jmeter聚合报告导出log文档,Jmeter聚合报告导出到CSV
Jmeter聚合报告导出log文档 在Filename中输入 EKS_perf_log\\${type}_log\\${__P(UNIQUEID,${__time(YMDHMS)})}\all-graph-results-log.csv 可以得到执行的log,文件夹包含时间戳 Jmeter聚合报告导出到CSV 点击Save Table Data,保存到CSV文件中...
mysqldump 参数详解
mysqldump 是一个用于备份 MySQL 数据库的工具。它可以生成一组 SQL 语句,这些语句可以用来重现原始数据库对象定义和表数据。以下是一些常用的 mysqldump 参数及其详细解释: 常用参数 基本参数 --host=host_name, -h host_name: 指定 MySQL 数据库主机地址,默认为 localh…...
DeepSeek R1 简易指南:架构、本地部署和硬件要求
DeepSeek 团队近期发布的DeepSeek-R1技术论文展示了其在增强大语言模型推理能力方面的创新实践。该研究突破性地采用强化学习(Reinforcement Learning)作为核心训练范式,在不依赖大规模监督微调的前提下显著提升了模型的复杂问题求解能力。 …...
基于 MySQL 数据库对三级视图(用户视图、DBA视图、内部视图)的详细解释
基于 MySQL 数据库对三级视图(用户视图、DBA视图、内部视图)的详细解释,结合理论与实际操作说明: 一、三级视图核心概念 数据库的三级视图是 ANSI/SPARC 体系结构的核心思想,MySQL 的实现逻辑如下: …...
[Web 信息收集] Web 信息收集 — 手动收集 IP 信息
关注这个专栏的其他相关笔记:[Web 安全] Web 安全攻防 - 学习手册-CSDN博客 0x01:通过 DNS 服务获取域名对应 IP DNS 即域名系统,用于将域名与 IP 地址相互映射,方便用户访问互联网。对于域名到 IP 的转换过程则可以参考下面这篇…...
跨AWS账户共享SQS队列以实现消息传递
在现代分布式系统中,不同的服务和组件通常需要进行通信和协作。Amazon Simple Queue Service (SQS)提供了一种可靠、可扩展且完全托管的消息队列服务,可以帮助您构建分布式应用程序。本文将介绍如何在一个AWS账户(账户A)中创建SQS队列,并授权另一个AWS账户(账户B)中的用户和角色…...
DeepSeek 202502 开源周合集
DeepSeek 本周的开源项目体现了其在 AI 技术栈中的深厚积累,从硬件协同优化(FlashMLA)、通信库(DeepEP)、核心计算(DeepGEMM)到推理模型(DeepSeek-R1),覆盖了…...
springai系列(二)从0开始搭建和接入azure-openai实现智能问答
文章目录 前言1.从0开始搭建项目2.进入微软openai申请key3.配置application.yaml4.编写controller5.测试源码下载地址总结 前言 之前使用openai的官网的api需要科学上网,但是我们可以使用其他的代理间接实现使用chatgpt的相关模型,解决这个问题。比如:本…...
Apache部署Vue操作手册(SSL部分)
1. Apache配置(windows版本) 1.1 httpd.conf 配置 找到apache配置文件 httpd.conf,将下面两条文件的注释#去掉,如果没搜到就新增这两条配置。一个是开启ssl模块,一个是引用专门的ssl配置文件。 LoadModule ssl_modu…...
人类驾驶的人脑两种判断模式(反射和预判)-->自动驾驶两种AI模式
一种模式是直觉模式,判断是基于条件反射,视觉感知 触发到 直接条件反射(从经历中沉淀形成的神经信息闭环),类似现在自动驾驶技术的传统AI模式。 另一种模式是物理时空图式推理模式,判断是基于预判预测&…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...
多模态2025:技术路线“神仙打架”,视频生成冲上云霄
文|魏琳华 编|王一粟 一场大会,聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中,汇集了学界、创业公司和大厂等三方的热门选手,关于多模态的集中讨论达到了前所未有的热度。其中,…...
C++:std::is_convertible
C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...
macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用
文章目录 问题现象问题原因解决办法 问题现象 macOS启动台(Launchpad)多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显,都是Google家的办公全家桶。这些应用并不是通过独立安装的…...
Matlab | matlab常用命令总结
常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...
Angular微前端架构:Module Federation + ngx-build-plus (Webpack)
以下是一个完整的 Angular 微前端示例,其中使用的是 Module Federation 和 npx-build-plus 实现了主应用(Shell)与子应用(Remote)的集成。 🛠️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...
NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合
在汽车智能化的汹涌浪潮中,车辆不再仅仅是传统的交通工具,而是逐步演变为高度智能的移动终端。这一转变的核心支撑,来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒(T-Box)方案:NXP S32K146 与…...
Go 并发编程基础:通道(Channel)的使用
在 Go 中,Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式,用于在多个 Goroutine 之间传递数据,从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...
uniapp 字符包含的相关方法
在uniapp中,如果你想检查一个字符串是否包含另一个子字符串,你可以使用JavaScript中的includes()方法或者indexOf()方法。这两种方法都可以达到目的,但它们在处理方式和返回值上有所不同。 使用includes()方法 includes()方法用于判断一个字…...
Chrome 浏览器前端与客户端双向通信实战
Chrome 前端(即页面 JS / Web UI)与客户端(C 后端)的交互机制,是 Chromium 架构中非常核心的一环。下面我将按常见场景,从通道、流程、技术栈几个角度做一套完整的分析,特别适合你这种在分析和改…...
