【数据结构与算法】Dijkstra算法、Floyd算法

文章目录

  • 最短路径
    • Dijkstra算法
      • 实现
    • Floyd算法
      • 实现

最短路径

前文提到,对于不带权图来说,BFS可以解决最短路径问题,因为它是类似于树的层次遍历那样,同一个结点的后继的访问顺序相邻,这使得BFS首次访问结点时的路径必然是最短的。

但是对于带权图来说,BFS就无法解决了最短路径问题了,除非图中各边的权值相等。所以我们需要用一些其他的算法来解决带权图的最短路径问题。

最短路径问题大致上可以分为两类:一种是单源最短路径,即求图中某一点到其他任意一点的最短路径。另一种是求每对顶点之间的最短路径。

经典的求带权图的单源最短路径的算法有Dijkstra(迪杰斯特拉)算法,求每对顶点间的最短路径的算法有Floyd(弗洛伊德)算法。

Dijkstra和Floyd也能够解决不带权图中的最短路径问题,因为不带权图可以视为每一条边的权值都相同的带权图。

Dijkstra算法

Dijkstra基于贪心算法,它的核心是找出当前集合中顶点可达的,与源点最近的顶点,把它与源点之间的路径视为源点到该点的最短路径,并把它加入到集合中。

例如:

在这里插入图片描述

假设我们找从1到其他各个顶点的最短路径,那么按照Dijkstra算法:

初始顶点集 S = { 1 } S=\{1\} S={1}

顶点第一轮第二轮第三轮第四轮
21->2=5
31->3=61->3=6
41->2->4=71->2->4=7
51->2->5=171->2->5=171->2->4->5=15
61->3->6=91->3->6=9
71->2->4->7=11
8
S{1,2}{1,2,3}{1,2,3,4}
最短路径1->2=51->3=61->2->4=71->3->6=9

第一轮,集合S中的顶点可达的顶点有2、3。但1->2的距离最短,所以 1->2 的最短路径 = 5, 把2加入到S中,此时 S = { 1 , 2 } S=\{1,2\} S={1,2}

第二轮,集合S中的顶点可达的顶点有3、4、5。但1->3的距离最短,所以1->3的最短路径 = 6,把3加入到S中,此时 S = { 1 , 2 , 3 } S=\{1,2,3\} S={1,2,3}

第三轮,集合S中的顶点可达的顶点有4、5、6。但1-2->4的路径最短,所以1->4的最短路径 = 7,把4加入到S中,此时 S = { 1 , 2 , 3 , 4 } S=\{1,2,3,4\} S={1,2,3,4}

第四轮,集合S中的顶点可达的顶点有5、6、7。但1->3->6的路径是最短的,所以1->6的最短路径 = 9,把6加入到S中,此时 S = { 1 , 2 , 3 , 4 , 6 } S=\{1,2,3,4,6\} S={1,2,3,4,6}

后面依次类推,直到所有的顶点都被包括在S中。

实现

Dijkstra实际上是在利用贪心的原则更新维护源点与集合中的顶点可达的顶点之间的路径,并且每次选择一个未被加入到集合中且到源点距离最近的顶点,记录它和源点之间的距离为最短路径,把它加入到集合中。

也正是因为贪心的原则,所以Dijkstra无法处理带负权的的带权图。

例如:如果上图中的2—4之间的距离为-300,在第二轮,使用Dijkstra算法就已经确定了1–3的最短路径,但是实际上的最短路径 = 1->2->4->3 = 5-300+6 = -289

代码实现如下:

// C++

#include <vector>

const int INF = 0x3f3f3f3f;

/**
 * @param E 正权图的边集
 * @param source 源点
 */
std::vector<int> dijkstra(std::vector<std::vector<int>> E, int source)
{
    int n = E.size();
    std::vector<int> distance(n, INF);   // 顶点到源点的距离
    std::vector<bool> visited(n, false); // 顶点是否被加入到集合中。

    distance[source] = 0;

    for (int i = 0; i < n - 1; i++)
    {
        int cur = -1;
        // 选择一个到源点距离最近的顶点
        for (int j = 0; j < n; j++)
        {
            if (!visited[j] && (cur == -1 || distance[j] < distance[cur]))
                cur = j;
        }

        // 标记已加入到集合中
        visited[cur] = true;

        // 贪心原则,更新与它相邻的顶点的路径
        for (int j = 0; j < n; j++)
        {
            distance[j] = std::min(distance[j], distance[cur] + E[cur][j]);
        }
    }

    return distance;
}

Floyd算法

Floyd算法基于动态规划,它的核心是一个表达式: d i s t [ i ] [ j ] = m i n ( d i s t [ i ] [ j ] , d i s t [ i ] [ k ] + d i s t [ k ] [ j ] ) dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]) dist[i][j]=min(dist[i][j],dist[i][k]+dist[k][j]) d i s t [ i ] [ j ] dist[i][j] dist[i][j]表示当前状况下 i 到 j 的最短路径。

当然,只有这个表达式可能不容易理解,我们扩展一下

// 引入中间结点k,每次循环更新一次当前状况下的最短路径
for (int k = 0; k < n; k++)
{
    // 内层循环,更新当前状况下各顶点间的最短路径
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n; j++)
        {
            // 当前状况下i到j的最短路径 = min(i到j的路径, i到k的路径 + k到j的路径)。
            dist[i][j] = Math.min(dist[i][j], dist[i][k] + dist[k][j]);
        }
    }
}

很容易就能发现,Floyd算法实际上是一种非常简单暴力的求解算法。它通过枚举所有的情况,来获取每对顶点之间的最短路径。

实现

// C++

#include <vector>

/**
 * @param E 边集
 */
std::vector<std::vector<int>> floyd(std::vector<std::vector<int>> E)
{
    int n = E.size();
    std::vector dist(E);

    // 引入中间结点k,每次循环更新一次当前状况下的最短路径
    for (int k = 0; k < n; k++)
    {
        // 内层循环,更新当前状况下各顶点间的最短路径
        for (int i = 0; i < n; i++)
        {
            for (int j = 0; j < n; j++)
            {
                // 当前状况下i到j的最短路径 = min(i到j的路径, i到k的路径 + k到j的路径)。
                dist[i][j] = std::min(dist[i][j], dist[i][k] + dist[k][j]);
            }
        }
    }
    return dist;
}

由于Floyd算法的特性,因此Floyd可以处理带负权的带权图。

全篇结束,感谢阅读。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

CCRC-DSO数据安全官和CCRC-DSA数据安全评估师的差异

在当前时代背景下&#xff0c;随着中国数据安全政策的加速推进和相关法律法规的密集发布&#xff0c;对数据资产进行全生命周期的安全管控变得日益迫切。 这不仅成为企业可持续发展的必要条件&#xff0c;也引发了广泛的关注于数据安全相关的专业培训及认证领域。 在此环境下&a…

PS系统教程26

PS与BR的关系 如何把图片以图层的方式导入画板里面 选中三张图片/多张选择工具-PS-将文件载入PS图层意味着这三张图片以图层的方式嵌入PS中 拼接长图 裁剪图片 保存裁剪后的图片拼接图片选中要拼接的图片选择工具-PS-Photomerge(拼合图像&#xff09; 图像处理器 大白话&…

对撞指针技巧

对撞指针技巧 我们以LeetCode的一道题目来讲解一下对撞指针&#xff1b; LeetCode第27题移除元素&#xff0c;链接如下&#xff1a; https://leetcode.cn/problems/remove-element 如果使用快慢指针 如果使用快慢指针&#xff0c;将会有大量的后面元素赋值给前面元素的操作…

Open3D 点云随机下采样至指定数目

目录 一、背景 二、代码实现 2.1 关键函数 2.2完整代码 三、实现效果 3.1原始点云 3.2下采样后点云 3.3数据对比 一、背景 在Open3D中&#xff0c;如果你想将点云随机采样到固定的点数&#xff0c;可以使用random_down_sample 函数并计算相应的采样比例。以下是如何实现…

MySQL数据库(五):事务

MySQL数据库中的事务是一种用来保证一系列操作要么全部成功&#xff0c;要么全部取消的机制。想象一下你去超市购物&#xff0c;拿了很多商品&#xff0c;如果中途发现没带钱包&#xff0c;你可以放弃这次购买&#xff0c;所有商品会回到原位。通过事务&#xff0c;可以确保数据…

感恩父爱 健康同行 宁夏康源父亲节特惠普查

父亲&#xff0c;是那道坚实的屏障&#xff0c;为孩子们挡风遮雨。父亲&#xff0c;是那颗明亮的灯塔&#xff0c;为孩子们指明前进的方向。然而岁月无情&#xff0c;随着年龄的增长&#xff0c;曾经为我们遮风挡雨的父亲如今也逐渐进入了各种疾病的高发期。感恩父爱&#xff0…

linux下进度条的实现

一、代码一版 使用模块化编程 1.processbar.h #include<stdio.h> #define capacity 101 //常量使用宏定义 #define style //符号方便后续修改 extern void processbar();修饰变量的时候一定要带extern&#xff0c;修饰函数的时候可以省略&#xff0c;因为没有函数体就…

FLASH仿真EEPROM---基于智芯Z20K11XM

一、介绍 电可擦和可编程只读存储器(EEPROM)可以对字节或字编程和擦除。EEPROM中的数据即使断电也能保持&#xff0c;但Z20K1xx芯片不含EEPROM。然而&#xff0c;闪存可以通过EEPROM仿真软件来模拟EEPROM。Z20K1xx包含两个flash阵列。编程和擦除操作可以在一个数组上进行&#…

华为200人园区网有线和无线

实验描述&#xff1a; 1 内网有有线业务、内部无线、外部无线三种业误。 2 内网服务器配置静态IP&#xff0c;网关192.168.108.1。 3 sW1和R1之间使用v1an200 192.168.200.9/30 互联。 4 R2向运营商申请企业宽带并获得了1个固定公网IP&#xff1a; 200.1.1.1 子网掩码 255.255.…

双目相机测距原理

一、普通双目相机测距原理 普通双目相机具有如下特点&#xff1a;左右两个相机位于同一平面&#xff08;光轴平行&#xff09;&#xff0c;且相机参数&#xff08;焦距f&#xff09;一致。其原理图如下&#xff1a; 如图所示&#xff0c;P点为相应的物体位置&#xff0c;CL和C…

Java面试八股之JVM参数-XX:+UseCompressedOops的作用

JVM参数-XX:UseCompressedOops的作用 JVM参数-XX:UseCompressedOops的作用是启用对象指针压缩&#xff08;Ordinary Object Pointers compression&#xff09;。这一特性主要应用于64位的Java虚拟机中&#xff0c;目的是为了减少内存使用。在传统的64位系统中&#xff0c;对象…

分配自定义内存对齐的内存块的aligned_malloc实现分析

malloc一般使用当前平台默认的最大内存对齐数对齐内存&#xff0c;如MSVC在32bit下一般是8bit对齐&#xff1b;64位下则是16bit。这样对常规的数据来说没有问题。但如果我们自定义的内存对齐超出了这个范围&#xff0c;则不能直接使用malloc。当我们要分配一块具有特定内存对齐…

【乐吾乐2D可视化组态编辑器】图表动态显示

1. 添加数据 乐吾乐2D可视化组态编辑器地址&#xff1a;https://2d.le5le.com/ 图表动态展示是指一个图表图元的数据属性&#xff08;一般是dataY&#xff09;绑定多个变量&#xff0c;建立通信后数据动态变化展示。 官网默认Echarts图表拖拽到画布中是已经添加了图元的da…

OpenAI CTO米拉·穆拉提谈未来:AI一年半后达到博士水平

人工智能&#xff08;AI&#xff09;领域近年来的发展迅猛&#xff0c;特别是在大语言模型&#xff08;LLM&#xff09;的进步上。最近&#xff0c;OpenAI的首席技术官&#xff08;CTO&#xff09;米拉穆拉提&#xff08;Mira Murati&#xff09;在达特茅斯学院的一次采访中&am…

【Linux】Linux基础开发工具(yum)

Linux 软件包管理器 yum 什么是软件包 在Linux下安装软件, 一个通常的办法是下载到程序的源代码, 并进行编译, 得到可执行程序.但是这样太麻烦了, 于是有些人把一些常用的软件提前编译好, 做成软件包(可以理解成windows上的安 装程序)放在一个服务器上, 通过包管理器可以很方便…

​Python20 Numpy基础

NumPy&#xff08;Numerical Python&#xff09;是一个开源的Python库&#xff0c;广泛用于科学计算。它提供了一个高性能的多维数组对象&#xff0c;以及用于处理这些数组的工具和函数。NumPy是数据分析、机器学习、工程和科学研究中不可或缺的工具之一&#xff0c;因为它提供…

【数据结构与算法】最小生成树,Prim算法,Kruskal算法 详解

最小生成树的实际应用背景。 最节省经费的前提下&#xff0c;在n个城市之间建立通信联络网。 Kruskal算法&#xff08;基于并查集&#xff09; void init() {for (int i 1; i < n; i) {pre[i] i;} }ll root(ll a) {ll i a;while (pre[i] ! i) {i pre[i];}return i p…

PaddleOCR C++源码编译以及demo测试

Windows10下使用PaddleOCRc 1.所需要的环境 PaddleOCR 源码文件&#xff1a;https://gitee.com/paddlepaddle/PaddleOCR &#xff08;本文选择2.6https://github.com/PaddlePaddle/PaddleOCR/archive/refs/tags/v2.6.0.zip&#xff09; opencv库&#xff1a;https://opencv…

将 Cohere 与 Elasticsearch 结合使用

本教程中的说明向你展示了如何使用推理 API 使用 Cohere 计算嵌入并将其存储起来&#xff0c;以便在 Elasticsearch 中进行高效的向量或混合搜索。本教程将使用 Python Elasticsearch 客户端执行操作。 你将学习如何&#xff1a; 使用 Cohere 服务为文本嵌入创建推理端点&…

swiper 幻灯片

index.html <!DOCTYPE html> <html lang"en"> <head> <meta charset"utf-8"> <title>swiper全屏响应式幻灯片代码</title> <meta name"viewport" content"widthdevice-width, initial-scale1, min…