搜索与图论- Dijkstra 算法

news2024/11/29 12:52:33

文章目录

  • 一、Dijkstra 算法
    • 1. 简介
    • 2. 基本思想
    • 3. 朴素 Dijkstra 算法(重点)
      • 3.1 朴素 Dijkstra 算法实现步骤
      • 3.2 朴素 Dijkstra 算法伪代码
    • 4. 朴素 Dijkstra 算法具体实现详见例题 Dijkstra 求最短路 I 。
    • 5. 堆优化朴素 Dijkstra 算法
    • 6. 堆优化 Dijkstra 算法具体实现详见例题 Dijkstra 求最短路 II 。
    • 7. 堆优化 Dijkstra 算法和朴素 Dijkstra 算法时间复杂度
  • 二、Dijkstra 算法例题——Dijkstra 求最短路 I
    • 具体实现
      • 1. 样例演示
      • 2. 实现思路
      • 3. 代码注解
      • 4. 实现代码
  • 三、Dijkstra 算法例题——Dijkstra求最短路 II
    • 具体实现
      • 1. 样例演示
      • 2. 实现思路
      • 3. 代码注解
      • 4. 实现代码

一、Dijkstra 算法

1. 简介

  • Dijkstra(迪杰斯特拉)算法是由荷兰计算机科学家狄克斯特拉于1959 年提出的,因此又叫狄克斯特拉算法。
  • 这是从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题。
  • 迪杰斯特拉算法主要特点是从起始点开始,采用 贪心算法 的策略,每次遍历到始点距离最近且未访问过的顶点的邻接节点,直到扩展到终点为止。
  • 此算法不能用于求负权图,要求所有边的权重都为非负值。

2. 基本思想

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

3. 朴素 Dijkstra 算法(重点)

3.1 朴素 Dijkstra 算法实现步骤

  • (1) 各个点状态初始化,各点之间距离初始化。
  • 用一个 dist 数组保存源点到其余各个节点的距离,dist[i] 表示源点到节点 i 的距离。初始时,dist 数组的各个元素为无穷大。
  • 用一个状态数组 state 记录是否找到了源点到该节点的最短距离,state[i] 如果为真,则表示找到了源点到节点 i 的最短距离,state[i] 如果为假,则表示源点到节点 i 的最短距离还没有找到。初始时,state 各个元素为假。

在这里插入图片描述

  • (2) 源点到源点的距离为 0,即 dist[1] = 0。

在这里插入图片描述

  • (3) 遍历 dist 数组,找到一个节点,这个节点是:没有确定最短路径的节点中距离源点最近的点。假设该节点编号为 i 。此时就找到了源点到该节点的最短距离,state[i] 置为 1 。

在这里插入图片描述

  • (4) 遍历 i 所有可以到达的节点 j,如果 dist[j] 大于 dist[i] 加上 i -> j 的距离,即 dist[j] > dist[i] + w[i][j](w[i][j] 为 i -> j 的距离) ,则更新 dist[j] = dist[i] + w[i][j] 。

在这里插入图片描述

  • (5) 重复 3 4 步骤,直到所有节点的状态都被置为 1。

在这里插入图片描述

  • (6) 此时 dist 数组中,就保存了源点到其余各个节点的最短距离。

在这里插入图片描述

3.2 朴素 Dijkstra 算法伪代码

int dist[n],state[n];
dist[1] = 0, state[1] = 1;
for(i:1 ~ n)
{
    t <- 没有确定最短路径的节点中距离源点最近的点;
    state[t] = 1;
    更新 dist;
}

4. 朴素 Dijkstra 算法具体实现详见例题 Dijkstra 求最短路 I 。

5. 堆优化朴素 Dijkstra 算法

  • 朴素 Dijkstra 算法的主要耗时的步骤是从 dist 数组中选出,没有确定最短路径的节点中距离源点最近的点 t 。只是找个最小值而已,没有必要每次遍历一遍 dist 数组。
  • 在一组数中每次能很快的找到最小值,很容易想到使用小根堆。可以使用库中的小根堆(推荐)或者自己编写。
  • 具体思路如下:
  • (1) 起始点的距离初始化为零,其他点初始化成无穷大。
  • (2) 将起始点放入堆中。
  • (4) 不断循环,直到堆空。每一次循环中执行的操作为:弹出堆顶(与朴素版diijkstra找到S外距离最短的点相同,并标记该点的最短路径已经确定),用该点更新临界点的距离,若更新成功就加入到堆中。

6. 堆优化 Dijkstra 算法具体实现详见例题 Dijkstra 求最短路 II 。

7. 堆优化 Dijkstra 算法和朴素 Dijkstra 算法时间复杂度

  • 朴素 Dijkstra 算法时间复杂度 O(n2) 。
for (int i = 0; i < n; i++) 
{

    int t = -1;
    for (int j = 1; j <= n; j++)
    {
        if (!st[j] && (t == -1 || dist[j] < dist[t]))
            t = j; // O(n) * O(n) -> O(n^2)
    }

    st[t] = true; // O(n) * O(1) -> O(n)

    for (int j = 1; j <= n; j++)
    {
        dist[j] = min(dist[j], dist[t] + g[t][j]); //O(n) * O(n) -> O(n^2)
    }
}
  • 堆优化 Dijkstra 算法时间复杂度 O(mlog(n)) 。
//遍历大部分的边
    while (heap.size()) 
    {
        auto t = heap.top(); //O(m) * O(1) -> O(m)
        heap.pop();

        int ver = t.second, distance = t.first;
        if (st[ver])
        {
            continue;
        }
        st[ver] = true;  //O(m) * O(1) -> O(m)

        for (int i = h[ver]; i != -1; i = ne[i])
        {
            int j = e[i];
            if (dist[j] > distance + w[i]) 
            {
                dist[j] = distance + w[i];
                heap.push({dist[j], j});
                // 堆的插入操作时间复杂度是 O(log(n))
                // O(m) * O(log(n)) -> O(mlog(n))
            }
        }
    }

二、Dijkstra 算法例题——Dijkstra 求最短路 I

题目描述

给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环,所有边权均为正值。
请你求出 1 号点到 n 号点的最短距离,如果无法从 1 号点走到 n 号点,则输出 −1。

输入格式

第一行包含整数 n 和 m。
接下来 m 行每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z。

输出格式

输出一个整数,表示 1 号点到 n 号点的最短距离。
如果路径不存在,则输出 −1。

数据范围

1 ≤ n ≤ 500
1 ≤ m ≤ 1e5
图中涉及边长均不超过10000 。

输入样例

3 3
1 2 2
2 3 1
1 3 4

输出样例

3

具体实现

1. 样例演示

  • 输入 n = 3,m = 3,表示求从 1 号点到 n = 3 号点的最短距离,共有 m = 3 条边。
  • 从 1 号点到 2 号点的边长为 2 。
  • 从 2 号点到 3 号点的边长为 1 。
  • 从 1 号点到 3 号点的边长为 4 。

在这里插入图片描述

  • 显然,1 + 2 小于 4 ,因此最短路径存在,且为 3 。

2. 实现思路

  • 在此只作简单叙述,具体可见朴素 Dijkstra 算法实现步骤和样例演示。
  • (1) 初始化各点到起点距离为正无穷。
  • (2) 将 1 号点到起点的距离更新为 0 。
  • (3) 2 号点到 1 号点的距离为 2 ,小于正无穷,将其更新为 2 ; 3 号点同理,将距离更新为 4 。
  • (4) 然后,2 和 4 的最小值是 2 ,便将 2 加入集合 s ,此时,2 号点到 3 号点的距离 1 。可得,2 + 1 < 4,便将 3 号点到起点的距离更新为 3 。

3. 代码注解

  • int g[N][N];稠密图一般使用邻接矩阵。
  • int dist[N];记录每个节点距离起点的距离。
  • bool st[N];true 表示已经确定最短路 属于 s 集合。
  • memset(dist, 0x3f, sizeof dist);所有节点距离起点的距离初始化为无穷大。
  • dist[1] = 0;起点距离自己的距离为零。
  • int t = -1;一开始 t 的赋值是 -1 ,如果 t 没有被更新,那么要更新一下 t 。
  • memset 按字节赋值,所以 memset 0x3f 就等价与赋值为 0x3f3f3f3f 。
  • 其他代码注解已标注在实现代码当中,详见实现代码。

4. 实现代码

#include <bits/stdc++.h>
using namespace std;

const int N = 510;

int n, m;
int g[N][N];
int dist[N];
bool st[N];

int dijkstra()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;
    
    //迭代n次,每次可以确定一个点到起点的最短路
    for (int i = 0; i < n - 1; i ++ )
    {
        int t = -1;
        for (int j = 1; j <= n; j ++ )
        {
            //不在s集合,并且
            //如果没有更新过,则进行更新, 或者发现更短的路径,则进行更新
            if (!st[j] && (t == -1 || dist[t] > dist[j]))
            {
                t = j;
            }
        }
        
        //找到了距离最小的点t,并用最小的点t去更新其他的点到起点的距离
        for (int j = 1; j <= n; j ++ )
        {
            dist[j] = min(dist[j], dist[t] + g[t][j]);
        }
        
        //加入到s集合中
        st[t] = true;
    }
    
    // 如果起点到达不了n号节点,则返回-1
    if (dist[n] == 0x3f3f3f3f) 
    {
        return -1;
    }
    
    // 返回起点距离n号节点的最短距离
    return dist[n];
}

int main()
{
    cin >> n >> m;

    memset(g, 0x3f, sizeof g);
    while (m -- )
    {
        int a, b, c;
        cin >> a >> b >> c;

        g[a][b] = min(g[a][b], c);
    }

    cout << dijkstra() << endl;
    system("pause");
    return 0;
}

三、Dijkstra 算法例题——Dijkstra求最短路 II

题目描述

给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环,所有边权均为非负值。
请你求出 1 号点到 n 号点的最短距离,如果无法从 1 号点走到 n 号点,则输出 −1。

输入格式

第一行包含整数 n 和 m。
接下来 m 行每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z。

输出格式

输出一个整数,表示 1 号点到 n 号点的最短距离。
如果路径不存在,则输出 −1。

数据范围

1 ≤ n,m ≤ 1.5×1e5
图中涉及边长均不小于 0,且不超过 10000。
数据保证:如果最短路存在,则最短路的长度不超过 1e9。

输入样例

3 3
1 2 2
2 3 1
1 3 4

输出样例

3

具体实现

1. 样例演示

  • 同 Dijkstra 算法例题——Dijkstra求最短路 I 。

2. 实现思路

  • 不需要手写堆,使用 C++ 当中的优先队列。
  • 小根堆使用 greater 实现。

3. 代码注解

  • typedef pair<int, int> PII;<离起点的距离, 节点编号>。
  • memset(dist, 0x3f, sizeof dist);所有距离初始化为无穷大。
  • dist[1] = 0;1 号节点距离起点的距离为 0 。
  • priority_queue<PII, vector, greater> heap;建立一个小根堆。
  • 其他代码注解已标注在实现代码当中,详见实现代码。

4. 实现代码

#include <bits/stdc++.h>
using namespace std;

typedef pair<int, int> PII;//<离起点的距离, 节点编号>

const int N = 1e6 + 10;

int n, m;
int h[N], w[N], e[N], ne[N], idx;
int dist[N];
bool st[N];

//在 a 节点之后插入一个 b 节点,权重为 c
void add(int a, int b, int c)
{
    e[idx] = b;
    w[idx] = c;
    ne[idx] = h[a];
    h[a] = idx;
    idx ++ ;
}

int dijkstra()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;
    priority_queue<PII, vector<PII>, greater<PII>> heap;
    //1号节点插入堆
    heap.push({0, 1});

    while (heap.size())
    {
        //取出堆顶顶点
        auto t = heap.top();
        //并删除
        heap.pop();
        
        //取出节点编号和节点距离
        int ver = t.second;
        int distance = t.first;
        
        //如果节点被访问过则跳过
        if (st[ver]) 
        {
            continue;
        }
        st[ver] = true;

        for (int i = h[ver]; i != -1; i = ne[i])
        {
            //取出节点编号
            int j = e[i];
            if (dist[j] > dist[ver] + w[i])
            {
                dist[j] = dist[ver] + w[i];
                heap.push({dist[j], j});
            }
        }
    }

    if (dist[n] == 0x3f3f3f3f)
    {
        return -1;
    }
    return dist[n];
}

int main()
{
    cin >> n >> m;

    memset(h, -1, sizeof h);
    while (m -- )
    {
        int a, b, c;
        cin >> a >> b >> c;
        add(a, b, c);
    }

    cout << dijkstra() << endl;
    system("pause");
    return 0;
}

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

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

相关文章

cookie、sessionStorage和localStorage的区别(二)

cookie、sessionStorage和localStorage的区别&#xff08;一&#xff09;详细精炼知识调用前言引入核心干货webstorage本地存储cookiesessionStoragelocalStorage知识调用 文章中可能用到的知识点前端学习&#xff1a;浏览器缓存方式有哪些&#xff08; cookie localstorage s…

rocketmq源码学习-broker启动

前言 这篇笔记记录broker启动的源码学习 broker主要完成一下几件事情&#xff1a; 1.接收producer的发送请求&#xff0c;并对消息进行持久化、同步其他节点 2.接收consumer读取消息星球 3.定时向nameSrv注册心跳信息&#xff0c;保持连接 在启动的时候&#xff0c;也是分了…

Ant Design 6.0.0 实践集合

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 使用的6.0.0 beta版本 文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结前言 Ant Design 简称为 Antd antd 为 Web 应用提供了丰富的基础 U…

操作指南|通过JumpServer实现Kubernetes运维安全审计

本文重点介绍如何通过JumpServer实现Kubernetes的运维安全审计。此前&#xff0c;我们专门介绍过在Kubernetes集群上快速部署JumpServer的方法步骤&#xff0c;可参见《操作指南&#xff5c;在Kubernetes集群上快速部署JumpServer开源堡垒机》一文。 一、Kubernetes运维审计现…

ABP Vnext 学习03-授权中心微信小程序登录

前言 小程序开发的 前置条件 1 需要服务端是https 和域名 Ip 是不可以的 2 需要申请appid 小程序的官方流程图 个人理解 对于上面的流程图 步骤一 客户端 小程序调用wx.login 方法 获取用户的code 这个code 是限时的五分钟就会过期 拿到code 就可以向服务端发起登录请求了 …

vue3中ref的作用及ref和reactive之间的转化

ref的作用&#xff1a; &#xff08;1&#xff09;第一个作用&#xff1a;和vue一样&#xff1a; 绑在dom节点上拿到的是dom节点&#xff1b;绑在组件上拿到的是组件对象&#xff1b; 定义方式&#xff1a; <template><div><input type"text" ref&…

java运行数据区域分布

Java在运行程序过程中&#xff0c;会将自己的内存划分为若干个不同的数据数据区域&#xff0c;这些若干个区域&#xff0c;每个区域都有自己的用途&#xff0c;具体看下图 java是面向对象的语言&#xff0c;那么虚拟机中的数据&#xff08;对象&#xff09;是怎么被创建出来的呢…

加密 笔记

文章目录简单异或加密对称加密DES加密AES加密1.简单的加密解密逻辑2.填充方式**noPadding**3.加密模式1、**ECB模式&#xff08;默认&#xff09;**2、**CBC模式**3、CFB模式4、OFB模式5、CTR模式代码案例ECB加密和CBC加密测试非对称加密RSA加密AES和RSA混合加密哈希散列算法什…

[附源码]Python计算机毕业设计动物保护资讯推荐网站Django(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程 项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等…

RabbitMQ[3]-RabbitMQ如何保证消息的可靠性投递与消费?

上篇文章&#xff1a;RabbitMQ的核心概念有哪些&#xff1f;它们的职责是什么&#xff1f;中我们详细介绍了RabbitMQ的工作模式&#xff0c;根据它的工作模式&#xff0c;一条消息从生产者发出&#xff0c;到消费者消费&#xff0c;需要经历以下4个步骤&#xff1a; 生产者将消…

Java笔记——String类各种方法的使用总结(附带实例)

String类的获取方法 String类实现获取功能的方法有 int length() —— 获取字符串长度 char charAt(int index) —— 获取指定索引处的字符值 int indexOf(int ch) —— 获取指定字符第一次出现的索引位置 int indexOf(String str) —— 获取指定字符串第一次出现的索引位…

ArrayDeque源码解析

ArrayDeque源码解析 问题 &#xff08;1&#xff09;什么是双端队列&#xff1f; &#xff08;2&#xff09;ArrayDeque 是怎么实现双端队列的&#xff1f; &#xff08;3&#xff09;ArrayDeque 是线程安全的吗&#xff1f; &#xff08;4&#xff09;ArrayDeque 是有界的…

【正点原子FPGA连载】 第三十五章双目OV5640摄像头HDMI显示实验 摘自【正点原子】DFZU2EG/4EV MPSoC 之FPGA开发指南V1.0

1&#xff09;实验平台&#xff1a;正点原子MPSoC开发板 2&#xff09;平台购买地址&#xff1a;https://detail.tmall.com/item.htm?id692450874670 3&#xff09;全套实验源码手册视频下载地址&#xff1a; http://www.openedv.com/thread-340252-1-1.html 第三十五章双目O…

基于jsp+mysql+ssm二手书交易管理系统-计算机毕业设计

项目介绍 这样一个二手书交易网站为用户提供了一个可以在网上买卖图书的平台&#xff0c;用户可以通过二手书交易管理系统进行注册或登录操作&#xff0c;登录成功后可以查看自己已发布的售书信息或者求购信息。同时&#xff0c;用户可以浏览其他用户发布的售书信息和求购信息…

基于51单片机的多层电梯(1-16层)运行系统仿真设计_层数可改

基于51单片机的多层电梯(1-16层)运行系统仿真设计_层数可改 仿真图proteus 8.9 程序编译器&#xff1a;keil 4/5 编程语言&#xff1a;C语言 设计编号&#xff1a;S0027 视频演示 基于51单片机的多层电梯(1-16层)运行系统仿真设计演示视频主要功能&#xff1a; 结合实际情…

[附源码]Python计算机毕业设计SSM基于web的学生社团管理系统(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

[附源码]Python计算机毕业设计SSM基于的二手房交易系统(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 Ma…

基于Java+SQL Server2008开发的(WinForm)个人财物管理系统【100010036】

一、需求分析 个人财务管理系统是智能化简单化个人管理的重要的组成部分。并且随着计算机技术的飞速发展&#xff0c;计算机在管理方面应用的旁及&#xff0c;利用计算机来实现个人财务管理势在必行。本文首先介绍了个人财务管理系统的开发目的&#xff0c;其次对个人财务管理…

2022年12月中国数据库排行榜:OceanBase立足创新登榜首,华为腾讯排名上升树雄心

不经一番寒彻骨&#xff0c;怎得梅花扑鼻香。 2022年12月的 墨天轮中国数据库流行度排行榜 火热出炉&#xff0c;本月共有249个数据库参与排名&#xff0c;相比上月新增3个数据库。本月排行榜前十用一句话可以概括为&#xff1a;榜单前十一片红&#xff0c;TODO 格局重洗牌&…

[附源码]Python计算机毕业设计SSM基于web技术的米其林轮胎管理系统(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…