SPFA + 链式前向星建图【附Java模板】

news2024/12/26 1:18:35

在这里插入图片描述

                                                                             SPFA算法是什么?它能解决什么样的问题?          


🌷 仰望天空,妳我亦是行人.✨
🦄 个人主页——微风撞见云的博客🎐
🐳 数据结构与算法专栏的文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺
🪁 希望本文能够给读者带来一定的帮助🌸文章粗浅,敬请批评指正!🐥


文章目录

  • 🦩SPFA算法的概念
  • 🐸SPFA和Dijkstra的区别
  • 🐳SPFA算法的解题步骤
  • 🦕模板题:["随机数据下的最短路问题"](https://www.lanqiao.cn/problems/1366/learning/?page=1&first_category_id=1&sort=students_count&tags=SPFA)
  • 🐳结语


🦩SPFA算法的概念

🍐SPFA算法(Shortest Path Faster Algorithm)是一种单源最短路径算法,用于求解带权有向图中某个源点到其他所有点的最短路径。它是对Bellman-Ford算法的优化,通过使用队列来避免重复松弛操作,从而提高了算法的效率。SPFA算法的时间复杂度为O(kE),其中k是一个常数,通常情况下k小于2,因此SPFA算法的时间复杂度可以认为是O(E)


🐸SPFA和Dijkstra的区别

🍋 同样是单源最短路径算法,它和Dijkstra有什么区别?

    🍒 SPFA(Shortest Path Faster Algorithm)Dijkstra算法都是用于解决单源最短路径问题的算法,但它们有以下几个区别

        🪁1.时间复杂度:Dijkstra算法的时间复杂度为O(ElogV),其中E为边数,V为顶点数;而SPFA算法的时间复杂度为O(kE),其中k为常数,一般情况下k小于2,但在最坏情况下,k可以达到V。

        🪁2.实现方式:Dijkstra算法使用堆优化的贪心策略,每次选择当前距离最小的未访问节点进行松弛操作;而SPFA算法使用队列实现,每次选择当前距离最小的未访问节点进行松弛操作。

        🪁3.稳定性:Dijkstra算法对于边权值为负的图无法处理,而SPFA算法可以处理负权边,但是在存在负环的情况下,SPFA算法会进入死循环

        🪁综上,Dijkstra算法适用于边权值为正的图,时间复杂度较低;而SPFA算法适用于边权值为负的图,但时间复杂度较高,且存在负环的情况下会出现问题


🐳SPFA算法的解题步骤

        SPFA算法的解题步骤如下:

🦕1. 初始化:将起点的距离设为0,其余点的距离设为无穷大,将起点加入队列。

🦕2. 进行松弛操作:从队列中取出一个点,遍历其所有邻居节点,如果通过该点可以使邻居节点的距离更短,则更新邻居节点的距离,并将其加入队列中。

🦕3. 重复步骤2,直到队列为空。

🦕4. 如果终点的距离被更新过,则说明存在从起点到终点的路径,输出最短路径长度。

🦕5. 如果终点的距离没有被更新过,则说明不存在从起点到终点的路径。

需要注意的是,为了避免负权边导致的死循环,需要在每次更新距离时判断是否存在负权环,如果存在则说明最短路径不存在。


🦕模板题:“随机数据下的最短路问题”

题目描述
给定 N 个点和 M 条单向道路,每条道路都连接着两个点,每个点都有自己编号,分别为 1 ~ N 。
问你从 S 点出发,到达每个点的最短路径为多少。
输入描述
输入第一行包含三个正整数 N,M,S。
第 2 到 M + 1 行每行包含三个正整数 u,v,w,表示 u→v 之间存在一条距离为 w 的路。
1≤N≤5×10^3 , 1≤M≤5×10^4 , 1≤ui,vi≤N , 0≤wi≤10^9
本题数据随机生成。
输出描述
输出仅一行,共 N 个数,分别表示从编号 S 到编号为 1 ~ N 点的最短距离,两两之间用空格隔开。(如果无法到达则输出 -1)
输入输出样例 示例 1
输入
3 3 1
1 2 1
1 3 5
2 3 2
输出
0 1 3
运行限制
最大运行时间:1s 最大运行内存: 128M

        🦕【模板Code(含判断负环)】

import java.io.*;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Deque;


public class Main {
    static int N = (int) (5e3 + 10), M = (int) (5e4 + 10);
    static int[] head = new int[M], ends = new int[M], next = new int[M], weights = new int[M];//链式前向星
    static boolean[] st = new boolean[N];//表示某个点是否在队列里面,注意并非是否访问过
    static int n, m, s, total;
    static long[] dist = new long[N];
    static long[] cnt = new long[N];//判断是否负环回路;
    static StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));

    static void add(int start, int end, int weight) {
        ends[total] = end;
        weights[total] = weight;
        next[total] = head[start];
        head[start] = total++;
    }

    public static void main(String[] args) throws IOException {
        n = nextInt();
        m = nextInt();
        s = nextInt();
        Arrays.fill(head, -1);
        for (int i = 0; i < m; i++) add(nextInt(), nextInt(), nextInt());
        if (SPFA()) System.out.println("存在负环回路");
        else System.out.println("无负环回路");
        for (int i = 1; i <= n; i++) {
            if (dist[i] == Long.MAX_VALUE) out.print("-1" + " ");
            else out.print(dist[i] + " ");
        }
        out.flush();
    }


    static boolean SPFA() {
        Deque<Integer> queue = new ArrayDeque<>();
        Arrays.fill(dist, Long.MAX_VALUE);
        dist[s] = 0;//初始点
        queue.offer(s);
        st[s] = true;
        while (!queue.isEmpty()) {
            int hh = queue.poll();
            st[hh] = false;
            for (int i = head[hh]; i != -1; i = next[i]) {
                int j = ends[i];
                if (dist[j] > dist[hh] + weights[i]) {
                    dist[j] = dist[hh] + weights[i];
                    cnt[j] = cnt[hh] + 1;
                    if (cnt[j] >= n) return true;//判断是否负环回路;
                    if (!st[j]) {// 当前已经加入队列的结点,无需再次加入队列,即便发生了更新也只用更新数值即可,重复添加降低效率
                        queue.offer(j);
                        st[j] = true;
                    }
                }
            }
        }
        return false;
    }

    static int nextInt() throws IOException {
        in.nextToken();
        return (int) in.nval;
    }

}

        🦕【解题Code_AC】

import java.io.*;
import java.util.*;


public class Main {
    static int N = (int) (5e3 + 10), M = (int) (5e4 + 10);
    static int[] head = new int[M], ends = new int[M], next = new int[M], weights = new int[M];
    static boolean[] st = new boolean[N];
    static int n, m, s, total;
    static long[] dist = new long[N];
    static StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));


    static void add(int start, int end, int weight) {
        ends[total] = end;
        weights[total] = weight;
        next[total] = head[start];
        head[start] = total++;
    }

    public static void main(String[] args) throws IOException {
        n = nextInt();
        m = nextInt();
        s = nextInt();
        Arrays.fill(head, -1);
        for (int i = 0; i < m; i++) add(nextInt(), nextInt(), nextInt());
        SPFA();
        for (int i = 1; i <= n; i++) {
            if (dist[i] == Long.MAX_VALUE) out.print("-1" + " ");
            else out.print(dist[i] + " ");
        }
        out.flush();
    }


    static void SPFA() {
        Deque<Integer> queue = new ArrayDeque<>();
        Arrays.fill(dist, Long.MAX_VALUE);
        dist[s] = 0L;
        queue.offer(s);
        st[s] = true;
        while (!queue.isEmpty()) {
            int hh = queue.poll();
            st[hh] = false;
            for (int i = head[hh]; i != -1; i = next[i]) {
                int j = ends[i];
                if (dist[j] > dist[hh] + weights[i]) {
                    dist[j] = dist[hh] + weights[i];
                    if (!st[j]) {
                        queue.offer(j);
                        st[j] = true;
                    }
                }
            }
        }
    }

    static int nextInt() throws IOException {
        in.nextToken();
        return (int) in.nval;
    }

}

在这里插入图片描述


🐳结语

    🐬初学一门技术时,总有些许的疑惑,别怕,它们是我们学习路上的点点繁星,帮助我们不断成长。

    🐟文章粗浅,希望对大家有帮助!

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

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

相关文章

P3029 [USACO11NOV]Cow Lineup S 双指针 单调队列

“五一”小长假来了趟上海&#xff0c;在倒数第二天终于有时间做了一会儿题目&#xff0c;A了之后过来写一篇题解 【问题描述】 农民约翰雇一个专业摄影师给他的部分牛拍照。由于约翰的牛有好多品种&#xff0c;他喜欢他的照片包含每个品种的至少一头牛。 约翰的牛都站在一条沿…

[MAUI]模仿iOS应用多任务切换卡片滑动的交互实现

文章目录 原理创建布局创建分布函数创建动效创建绑定数据细节调整首张卡片的处理为卡片添加裁剪跳转到最后一张卡片 项目地址 看了上一篇博文的评论&#xff0c;大家对MAUI还是比较感兴趣的&#xff0c;非常感谢大家的关注&#xff0c;这个专栏我争取周更&#x1f609;。 App之…

华为OD机试真题(Java),数组拼接(100%通过+复盘思路)

一、题目描述 现在有多组整数数组,需要将它们合并成一个新的数组。 合并规则,从每个数组里按顺序取出固定长度的内容合并到新的数组中,取完的内容会删除掉,如果该行不足固定长度或者已经为空,则直接取出剩余部分的内容放到新的数组中,继续下一行。 二、输入描述 第一…

【Python入门篇】——PyCharm的基础使用

作者简介&#xff1a; 辭七七&#xff0c;目前大一&#xff0c;正在学习C/C&#xff0c;Java&#xff0c;Python等 作者主页&#xff1a; 七七的个人主页 文章收录专栏&#xff1a; Python入门&#xff0c;本专栏主要内容为Python的基础语法&#xff0c;Python中的选择循环语句…

Mysql Sharding-JDBC读写分离

0 课程视频 深入Sharding-JDBC分库分表从入门到精通【黑马程序员】_哔哩哔哩_bilibili 1 基本概念 1.1应用逻辑 1.1.1 msyql 多库 多表 多服务器 1.1.2 通过Sharding-JDBC jar包->增强JDBC 访问多数据源 -> 自动处理成一个数据源 1.1.3 使用数据的人 -> 使用Sh…

Python+Yolov5墙体裂缝识别

程序示例精选 PythonYolov5墙体裂缝识别 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对<<PythonYolov5墙体裂缝识别>>编写代码&#xff0c;代码整洁&#xff0c;规则&#xff0c…

C++类的默认成员函数

文章目录 默认函数构造函数和析构函数构造函数析构函数 拷贝构造函数运算符重载赋值运算符重载赋值运算符重载和构造函数 默认函数 什么是默认函数&#xff1f; 默认函数就是当你使用这个类对象时,这个类会自动调用的函数C中有六个默认成员函数,并且作用各不相同,下面我们来一…

2 ROS2话题通讯基础

2 ROS2话题通讯基础 2.1 ROS2话题通讯介绍2.2 ROS2常用的消息类型介绍2.2.1 std_msgs消息类型2.2.2 geometry_msgs消息类型 2.3 使用C/C创建基础消息类型的话题通讯2.3.1 创建C/C发布话题信息的功能包并配置VSCode环境2.3.2 编写ROS2发布话题节点CPP文件2.3.3 配置C/C发布话题功…

数据结构学习记录——堆的插入(堆的结构类型定义、最大堆的创建、堆的插入:堆的插入的三种情况、哨兵元素)

目录 堆的结构类型定义 最大堆的创建 堆的插入 堆的插入的三种情况 代码实现 哨兵元素 堆的结构类型定义 #define ElementType int typedef struct HNode* Heap; /* 堆的类型定义 */ struct HNode {ElementType* Data; /* 存储元素的数组 */int Size; /* 堆中…

『python爬虫』08. 数据解析之bs4解析(保姆级图文)

目录 1. 安装bs4模块find()findall() 2. 爬取信息测试总结 欢迎关注 『python爬虫』 专栏&#xff0c;持续更新中 欢迎关注 『python爬虫』 专栏&#xff0c;持续更新中 1. 安装bs4模块 pip install bs4 pip install-i https://pypi.tuna.tsinghua.edu.cn/simplebs4如果遇到报…

[网络原理] HTTP协议

要珍惜时间呀 文章目录 1. HTTP协议概念2. HTTP协议格式2.URL3. GET与POST方法3.1 GET方法3.2 POST方法3.3 GET与POST的区别 1. HTTP协议概念 HTTP协议是应用层协议,TCP/IP协议为传输层协议,负责传输数据.而HTTP协议相当于对传输的数据据怎样处理和使用进行说明. 每次,我们访问…

Mininet+Ryu安装教程

最近要做一个Mininet的网络环境&#xff0c;网络设备由Mininet来模拟&#xff0c;SDN控制器用Ryu来做&#xff0c;为了避免每次重新做再去翻查资料&#xff0c;我在这里系统地整理一遍 硬件需求 我在 VMWare Workstation 16 Player虚拟机上运行的Ubuntu 22.04.1 硬件需求内存…

供应链 | 需求不确定情况下的物料需求规划: 基于随机优化的研究

作者&#xff1a;Simon Thevenin, Yossiri Adulyasak, Jean-Francois Cordeau​ 引用&#xff1a;Thevenin S, Adulyasak Y, Cordeau J F. Material requirements planning under demand uncertainty using stochastic optimization[J]. Production and Operations Management,…

react的项目实战 2

入口文件引入了app这个组件 app这个组件又引入了header这个组件。 然后外面引入这个组件 进行页面的显示 它不会影响到其他页面的组件的样式。 ​​​​​​​

面试必备:接口自动化测试精选面试题大全

目录 一、 请问你是如何做接口测试的&#xff1f; 二、接口测试如何设计测试用例&#xff1f; 三、接口测试执行中需要比对数据库吗&#xff1f; 四、接口测试质量评估标准是什么&#xff1f; 五、接口产生的垃圾数据如何清理 六、其他接口要先获取接口信息&#xff0c;如…

利用wenda实现本地多模态数据的知识获取和推理

近年来&#xff0c;大型语言模型&#xff08;LLM&#xff09;技术取得了令人瞩目的进展&#xff0c;为自然语言处理领域带来了巨大的变革&#xff0c;但是大多数LLM都面临着领域适应性的问题&#xff0c;因为它们使用的数据都是公开的数据&#xff0c;在国内&#xff0c;有很多…

Day960.架构现代化-微服务 -遗留系统现代化实战

架构现代化-微服务 Hi&#xff0c;我是阿昌&#xff0c;今天学习记录的是关于架构现代化-微服务的内容。 在自治气泡模式的基础上&#xff0c;通过事件拦截来实现数据同步&#xff0c;给气泡和遗留系统之间又加上 API 访问这个通信渠道。 这时的自治气泡就和真正的微服务差不…

Rust - 变量与数据的交互方式(move)

变量与数据的交互方式 - 移动 Rust 中的多个变量可以采用一种比较独特的方式和同一个数据进行交互&#xff0c;如下代码所示&#xff0c;将变量x的值赋给y&#xff1a; fn main() {let x 1;let y x; }我们大概可以推论出上述代码的原理&#xff1a;将1这个整数绑定给x变量&…

Mybatis读取和存储json类型的数据

目录 一、测试使用JSONObject来获取json二、设置TableName的autoResultMap为true&#xff0c;TableField的typeHandler为JacksonTypeHandler.class三、设置xml当中的resultMap四、JacksonTypeHandler讲解五、新增假如是JSONObject 不管数据库当中是以json还是longtext数据类型来…

树莓派Opencv调用摄像头(Raspberry Pi 11)

前言&#xff1a;本人初玩树莓派opencv&#xff0c;使用的是树莓派Raspberry Pi OS 11&#xff0c;系统若不一致请慎用&#xff0c;本文主要记录在树莓派上通过Opencv打开摄像头的经验。 1、系统版本 进入树莓派&#xff0c;打开终端输入以下代码&#xff08;查看系统的版本&…