数据结构之迪杰斯特拉算法

前言:前面两篇文章介绍了生成图的最小生成树的算法,接下来两篇文章会介绍图的最短路径的算法,迪杰斯特拉算法和弗洛伊德算法。迪杰斯特拉算法是用来计算一个点到其他所有点的最短路径,这个点称之为源点。

一、实现流程

回忆一下我们之前学习的 Prim 算法,构建最小生成树。从一个顶点出发,不断地寻找离当前生成树最近的节点,将节点加入到生成树。而寻找源点到其他节点的最短路径的 Dijkstra 算法,和 Prim 算法非常的类似。
因为假设当前5号节点和0号节点之间有两条路径,利用 Prim 算法,我们生成一个最小生成树,那么在最小生成树中,0 号节点和 5 号节点之间的路径一定是最短的,也就是 Dijkstra 算法要找的最短路径。所以整个实现流程非常的相似。和 Prim 一样,时间复杂度也是 O(v^2),只和顶点有关。
具体的实现流程大家可以参考 Prim算法,我就不赘述了,咱们来仔细看一下代码实现上有什么不一样

二、代码实现

1、初始化常量和数据类型

#define MAX_VEX 100
#define INFINITY INT_MAX // 使用 INT_MAX 代表无穷大// 图的结构体定义
typedef struct {int vexnum; // 节点数int arcnum; // 边数int arcs[MAX_VEX][MAX_VEX]; // 邻接矩阵
} MGraph;

首先我们要求源点到其他节点的最短路径 dist,就需要用一个数组,来保存当前源点到所有节点的路径长度。
我们需要一个数组来存储每个节点的 path,每次更新路径长度的时候,都需要更新当前节点的前驱结点。
另外为了防止对一个节点重复进行判断,我们需要一个数组 final,来记录一个节点是否已经找到最短路径 。

// 初始化
const int v0 = 0; // 将源点硬编码为0
int final[MaxVex]; // 记录节点有没有找到最短路径
int path[MaxVex]; // 记录每个节点的前驱结点
int dist[MaxVex]; // 记录源点到每个节点的最短路径

初始化的时候,根据邻接矩阵来初始化 dist 数组,为 0 号节点到其他节点的边的权值; final数组全部初始化为 0 表示都没有访问过,0号节点 final[0] 初始化为 1,表示已经访问过;path 数组和 0 号节点直接相连的节点的 path 记为 0 ,不直接相连的节点 path 记为 -1。

// --- 1. 初始化 ---for (int i = 0; i < G.vexnum; i++) {final[i] = 0;                // 所有顶点初始都未找到最短路径dist[i] = G.arcs[v0][i];     // v0 到各点的直连距离if (dist[i] < INFINITY && i != v0) {path[i] = v0;            // 如果有直连路径, 前驱为 v0} else {path[i] = -1;            // 否则无前驱}}dist[v0] = 0;   // 源点到自身的距离为 0final[v0] = 1;  // 源点已找到最短路径path[v0] = -1;  // 源点无前驱

接下来就是主要的处理过程。首先外层需要 G.vexnum - 1 次循环,因为每次只能找到一个最近节点。内层有两个操作需要循环:一个是找当前最近的节点;另一个是以找到的最近节点更新剩下的 dist 数组。
还要初始化一个 k 变量,用来记录每次找到的距离 0 号节点最近的节点

// --- 2. 主循环: 每次找到一个顶点的最短路径 ---
int k = -1; // k 用于记录当前找到的最近顶点的索引
for (int i = 1; i < G.vexnum; i++) { // 循环 n-1 次, 找出 n-1 个顶点的最短路int min = INFINITY;// a. 寻找当前离源点最近的未访问顶点 kfor (int j = 0; j < G.vexnum; j++) {if (!final[j] && dist[j] < min) {min = dist[j];k = j;}}// b. 将 k 标记为已找到最短路径final[k] = 1;// c. 以 k 为跳板, 更新其他未访问顶点的距离for (int j = 0; j < G.vexnum; j++) {// 如果 j 未被访问, 且经过 k 到达 j 的距离更短if (!final[j] && (long)dist[k] + G.arcs[k][j] < dist[j]) {dist[j] = dist[k] + G.arcs[k][j]; // 更新距离path[j] = k;                      // 更新前驱}}
}

三、整体代码

// 迪杰斯特拉算法 源点到其他节点的最短路径
#include <stdio.h>
#include <stdlib.h>
#include <limits.h> // 用于 INT_MAX#define MAX_VEX 100
#define INFINITY INT_MAX // 使用 INT_MAX 代表无穷大// 图的结构体定义
typedef struct {int vexnum; // 节点数int arcnum; // 边数int arcs[MAX_VEX][MAX_VEX]; // 邻接矩阵
} MGraph;// 迪杰斯特拉算法 (源点默认为0)
void Dijkstra(MGraph G) {const int v0 = 0; // 将源点硬编码为0int final[MAX_VEX]; // final[i]=1 表示已找到从 v0 到 vi 的最短路径int path[MAX_VEX];  // path[i] 记录 vi 的前驱顶点int dist[MAX_VEX];  // dist[i] 记录从 v0 到 vi 的当前最短路径长度// --- 1. 初始化 ---for (int i = 0; i < G.vexnum; i++) {final[i] = 0;                // 所有顶点初始都未找到最短路径dist[i] = G.arcs[v0][i];     // v0 到各点的直连距离if (dist[i] < INFINITY && i != v0) {path[i] = v0;            // 如果有直连路径, 前驱为 v0} else {path[i] = -1;            // 否则无前驱}}dist[v0] = 0;   // 源点到自身的距离为 0final[v0] = 1;  // 源点已找到最短路径path[v0] = -1;  // 源点无前驱// --- 2. 主循环: 每次找到一个顶点的最短路径 ---int k = -1; // k 用于记录当前找到的最近顶点的索引for (int i = 1; i < G.vexnum; i++) { // 循环 n-1 次, 找出 n-1 个顶点的最短路int min = INFINITY;// a. 寻找当前离源点最近的未访问顶点 kfor (int j = 0; j < G.vexnum; j++) {if (!final[j] && dist[j] < min) {min = dist[j];k = j;}}// b. 将 k 标记为已找到最短路径final[k] = 1;// c. 以 k 为跳板, 更新其他未访问顶点的距离for (int j = 0; j < G.vexnum; j++) {// 如果 j 未被访问, 且经过 k 到达 j 的距离更短if (!final[j] && (long)dist[k] + G.arcs[k][j] < dist[j]) {dist[j] = dist[k] + G.arcs[k][j]; // 更新距离path[j] = k;                      // 更新前驱}}}// --- 3. 打印结果 ---printf("从源点 %d 到各顶点的最短路径和距离如下:\n", v0);for (int i = 0; i < G.vexnum; i++) {if (i == v0) continue;printf("顶点 %d: 距离 = %-5d, 路径: ", i, dist[i]);int temp_path[MAX_VEX];int count = 0;int p = i;while (p != -1) {temp_path[count++] = p;p = path[p];}for (int j = count - 1; j >= 0; j--) {printf("%d", temp_path[j]);if (j > 0) printf(" -> ");}printf("\n");}
}int main() {MGraph G;// 初始化图G.vexnum = 6; // 6个顶点 (0, 1, 2, 3, 4, 5)G.arcnum = 11; // 11条边for (int i = 0; i < G.vexnum; i++) {for (int j = 0; j < G.vexnum; j++) {if (i == j) {G.arcs[i][j] = 0;} else {G.arcs[i][j] = INFINITY;}}}// 构建一个全连通的有向图的邻接矩阵G.arcs[0][1] = 50;G.arcs[0][2] = 10;G.arcs[0][4] = 45;G.arcs[0][5] = 100;G.arcs[1][2] = 15;G.arcs[1][4] = 10;G.arcs[2][0] = 20;G.arcs[2][3] = 15;G.arcs[3][1] = 20;G.arcs[3][4] = 35;G.arcs[4][3] = 30;G.arcs[5][3] = 3;Dijkstra(G); // 调用函数, 无需传入源点return 0;
}

其实和 Prim 算法只有一点不一样,就是更新其他未访问顶点的距离中的条件
Prim 算法是 if(lowcost[j]!=0 && G.arcs[k][j]<lowcost[j])
Dijkstra 算法是 if (!final[j] && (long)dist[k] + G.arcs[k][j] < dist[j])
前半句都是判断节点是否已经被访问过,只有后半句不一样
Prim 算法是生成最小生成树,当加入一个节点的时候,这个节点和树中的其他节点形成一个整体,因此只要 k 这个节点到 j 的距离更小,说明 j 离生成树的距离还可以更近,就更新 j 节点的路径
Dijkstra 算法是计算源点到其他节点的距离,所以必须对比从 源点到k+k到jdist[j]

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

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

相关文章

技术文档 | OpenAI 的 Kafka 演进之路与 Pulsar 迁移潜力

导读ChatGPT 用户量指数级暴涨&#xff0c;OpenAI 的 Kafka 集群在一年内增长 20 倍至 30 个集群[1]&#xff0c;其 Kafka 架构面临日均千亿级消息&#xff08;峰值 QPS 800万/秒&#xff09; 的压力。这揭示了一个关键事实&#xff1a;OpenAI 的成功不只依赖模型&#xff0c;更…

【bug】 jetson上opencv无法录制h264本地视频

在Jetson Orin NX上无法使用opencv直接录制h264/h265视频流&#xff08;h264格式的视频流才能在浏览器播放&#xff09; 解决&#xff1a; 软件编码&#xff1a;需要源码编译opencv 1.环境准备 pip uninstall opencv-python sudo apt install build-essential cmake git python…

解决http的web服务中与https服务交互的问题

问题背景&#xff1a; 需要在一个http的web服务中直接跟另一个https服务交互&#xff0c;不经过自身后端。 又来到了熟悉的跨域访问问题。 解决逻辑就是使用nginx转发&#xff0c;涉及到的文件也就是nginx.conf文件&#xff0c;前面解决minio链接时已经有经验了&#xff0c;但…

网站访问信息追踪系统在安全与性能优化中的关键作用——网络安全—仙盟创梦IDE

<?php // 收集访问信息 $visitorInfo未来之窗 [timestamp > date(Y-m-d H:i:s),ip > $_SERVER[REMOTE_ADDR] ?? unknown,page > $_SERVER[REQUEST_URI] ?? unknown,method > $_SERVER[REQUEST_METHOD] ?? unknown,user_agent > $_SERVER[HTTP_USER_A…

Oracle 时间处理函数和操作符笔记

前言 写sql时经常用到时间处理函数&#xff0c;我整理了一份Oracle的常用sql笔记,供大家参考。 如果对你有帮助&#xff0c;请点赞支持~ 多谢&#x1f64f; 笔记 -- 1. 获取当前日期和时间 -- SYSDATE, SYSTIMESTAMP, CURRENT_DATE, CURRENT_TIMESTAMP, LOCALTIMESTAMP SELE…

TDengine时序数据库 详解

1. TDengine 简介 TDengine 是一款 高性能、分布式、支持 SQL 的时序数据库&#xff08;Time-Series Database, TSDB&#xff09;&#xff0c;专为 物联网&#xff08;IoT&#xff09;、工业互联网、金融监控、日志分析 等场景设计。其核心特点包括&#xff1a; 超高性能&…

【IDEA】idea怎么修改注册的用户名称?

文章目录[toc]问题**方法 1&#xff1a;通过 JetBrains 账户网站修改****方法 2&#xff1a;通过 IDEA 内跳转修改&#xff08;快捷方式&#xff09;****注意事项****补充&#xff1a;修改 IDEA 内的项目级用户名**如何退出IDEA用户登录&#xff1f;问题 在 IntelliJ IDEA 中修…

AR眼镜重塑外科手术导航:精准“透视”新突破

在现代医学领域&#xff0c;增强现实&#xff08;AR www.teamhelper.cn &#xff09;技术正以前所未有的方式改变外科手术导航的面貌。通过为医生提供实时的三维可视化、精准的空间定位和智能交互功能&#xff0c;AR眼镜正在成为手术室中的重要工具。本文将系统介绍AR眼镜在手术…

服务端对接 HTTP 接口传输图片 采用base64还是 multipart/form-data

在服务端对接HTTP接口传输图片时&#xff0c;选择 multipart/form-data 还是 Base64 编码&#xff0c;需要根据具体场景权衡。以下是详细对比和建议&#xff1a;1. multipart/form-data 优点 更适合大文件传输&#xff1a; 直接以二进制流传输图片&#xff0c;无需编码/解码&am…

如何在 Windows 上安装 MongoDB 及常见问题

MongoDB 是一款 NoSQL 数据库&#xff0c;在数据管理和存储方面以其无与伦比的强大功能和多功能性而脱颖而出。该平台凭借其灵活性、可扩展性和高性能保持着领先优势&#xff0c;赢得了众多企业的信赖。在这方面&#xff0c;MongoDB 以及其在 Windows 操作系统中的表现&#xf…

JS与Go:编程语言双星的碰撞与共生

在编程语言的璀璨星河中&#xff0c;JavaScript&#xff08;简称JS&#xff09;与Go语言凭借各自独特的魅力&#xff0c;成为不同领域的佼佼者。前者以灵活多变的姿态征服了前端世界&#xff0c;后者则以高效稳健的特性在后端领域崭露头角&#xff0c;二者的碰撞与共生&#xf…

【开源】WpfMap:一个基于WPF(Windows Presentation Foundation)技术构建的数据可视化大屏展示页面

文章目录一、项目概述1.1 项目定位二、适用场景2.1 企业数据展示2.2 监控中心2.3 会议展示三、功能特性3.1 高度自定义3.2 实时更新3.3 丰富的可视化组件3.4 良好的用户体验四、技术资源4.1 开源地址一、项目概述 1.1 项目定位 WpfMap是一个基于WPF&#xff08;Windows Prese…

macbook安装homebrew

homebrew是什么&#xff1f;Homebrew 是 macOS&#xff08;以及 Linux&#xff09;上的一款包管理工具&#xff0c;被称为 “macOS 缺失的包管理器”&#xff0c;它能帮助用户轻松安装、卸载、更新各种命令行工具、开发环境、应用程序等。简单来说&#xff0c;它的作用类似手机…

ViLT: 无卷积或区域监督的视觉-语言Transformer

温馨提示&#xff1a; 本篇文章已同步至"AI专题精讲" ViLT: 无卷积或区域监督的视觉-语言Transformer 摘要 视觉与语言预训练&#xff08;Vision-and-Language Pre-training, VLP&#xff09;在多种联合视觉与语言的下游任务中显著提升了性能。目前的 VLP 方法在很…

初识决策树-理论部分

决策树 前言 参考了大佬的博客&#xff1a;博客地址 适合分析离散数据&#xff0c;若是连续数据需要转换成离散数据再做分析(比如图中的年龄) 结构 决策树由节点和有向边组成&#xff1b;节点可分为内部节点和叶节点 内部节点:特征叶节点:类别有向边:特征的取值范围 在用决…

opencv--day02--图像颜色处理及图像仿射变换

文章目录前言一、 图像颜色处理1. 颜色加法1.1 OpenCV加法1.2 numpy加法1.3 颜色加权加法2.颜色空间2.1 RGB颜色空间2.2 HSV颜色空间3. 颜色转换3.1 读取的图片同时转换3.2 对已有图片转换4. 图像灰度化4.1 灰度图概念4.2 最大值灰度化4.3 平均值灰度化4.4 加权均值灰度化5. 图…

第一层nginx访问url如何透传到第二层nginx

要让第一层Nginx将客户端请求的URL完整透传到第二层Nginx&#xff0c;关键在于正确配置proxy_pass指令及路径拼接规则。以下是具体配置方法和注意事项&#xff1a; 核心配置原则 proxy_pass指令末尾是否添加/会直接影响URL的透传方式&#xff1a; 不带/&#xff1a;会将locatio…

【2025最新毕业设计】外卖点餐小程序(外卖点餐管理系统)

外卖点餐小程序的设计与实现技术大纲&#xff08;Vue.js Element UI&#xff09;需求分析与功能设计用户需求调研&#xff1a;分析目标用户群体的核心需求&#xff08;如快速点餐、支付便捷、订单跟踪等&#xff09;核心功能模块划分&#xff1a;用户端&#xff08;登录/注册、…

两台电脑连接交换机,使用其中一台电脑的网络上网(NAT转发)

场景 windows 电脑和 linux电脑连在同一台交换机上&#xff0c;linux电脑有通过无线网络。要实现Windows电脑通过交换机共享Linux电脑的无线网络上网&#xff0c;需将Linux设为网关并进行网络共享&#xff0c;步骤如下&#xff1a; 一、Linux电脑设置&#xff08;网关配置&…

OpenCV Mat UMat GpuMat Matx HostMem InputArray等设计哲学

一、概览&#xff1a; GpuMat对应于cuda&#xff1b;HostMem 可以看作是一种特殊的Mat&#xff0c;其存储对应cuda在主机分配的锁页内存&#xff0c;可以不经显示download upload自动转变成GpuMat&#xff08;但是和GpuMat并无继承关系&#xff09;&#xff1b;UMat对应于openc…