当前位置: 首页 > news >正文

笔记:代码随想录算法训练营day65:dijkstra(堆优化版)精讲、Bellman_ford 算法精讲

学习资料:代码随想录

文中含大模型生成内容

dijkstra(堆优化版)精讲

卡码网:47. 参加科学大会

利用广度优先搜索去遍历图。使用list来存储图。将边按权值(将当前点的下一节点以及边的值)放入优先级队列,判定条件依然为minDis数组[当前](代表当前节点到源节点的最短距离)的值是否大于minDis[上一节点]

#include <iostream>
#include <vector>
#include <climits>    //包含各种整数类型的最大值和最小值
#include <queue>
#include <list>
using namespace std;
 
class mycompare{
public:
    bool operator()(const pair<int,int>& lhs,const pair<int,int>& rhs){
        return lhs.second>rhs.second;    //std::priority_queue 默认是大顶堆std::priority_queue<T, Container, Compare> 的 Compare 并不是直接定义排序规则,而是一个谓词,用于判断“谁的优先级更低”。
        //默认情况下,std::priority_queue 使用 std::less<T> 作为比较器,因此它构造的是一个 大顶堆(最大堆),即 大的元素优先级高。
        //但是,当我们自定义 Compare 时,我们的比较器 operator() 必须返回 true 表示 lhs 的优先级低于 rhs,即 rhs 应该排在 lhs 之前。
    }
};
 
struct Edge{
        int destination;
        int value;
 
        Edge(int e,int v):destination(e),value(v){};
};
 
int main(){
    int n,m,s,e,v;
    cin>>n>>m;
 
    vector<list<Edge>> traffic(n+1);
 
    for(int i=0;i<m;i++){
        cin>>s>>e>>v;
        traffic[s].push_back(Edge(e,v));
    }
 
    vector<int> minDis(n+1,INT_MAX);
    vector<bool> visited(n+1,false);
 
    priority_queue <pair<int,int>,vector<pair<int,int>>,mycompare> pq;
    int start = 1;
    int end = n;
 
    pq.push(pair<int,int>(start,0));
    minDis[start]=0;
 
    while(!pq.empty()){
        pair<int,int> cur =pq.top();   //优先级队列自动排好序了,所以不用再for去找了
        pq.pop();
 
        if(visited[cur.first]) continue;
 
        visited[cur.first]=true;
 
        for(Edge edges:traffic[cur.first]){
            if(!visited[edges.destination]&&minDis[cur.first]+edges.value<minDis[edges.destination]){
                minDis[edges.destination]=minDis[cur.first]+edges.value;
                pq.push(pair<int,int>(edges.destination,minDis[edges.destination]));
            }
        }
 
    }
 
    if(minDis[end]==INT_MAX) cout<<-1;
    else cout<<minDis[end];
}
  • 时间复杂度:O(ElogE) E 为边的数量
  • 空间复杂度:O(N + E) N 为节点的数量

比较函数定义有点绕,这个 operator() 的作用是:

  • lhs.second > rhs.second 时,返回 true,表示 lhs 的优先级低于 rhs,因此 rhs 应该排在前面(堆顶)。

  • 这样,priority_queue 就会把 second 值较小的元素放在堆顶,从而形成 小顶堆(最小堆)

bool operator()(const pair<int, int>& lhs, const pair<int, int>& rhs) { return lhs.second > rhs.second; }是c++中的仿函数写法。在 priority_queue 里,我们传入的是 比较器类MyComparator),它没有普通的函数,而是 重载了 operator(),所以可以像函数一样使用。

2. 为什么不用普通函数,而是用 operator()

因为 priority_queue 需要一个可调用的比较器对象,而 STL 容器一般不接受裸函数(bool cmp(pair<int, int>, pair<int, int>))。
只能传递:

  1. 仿函数(重载 operator() 的类)

  2. Lambda 表达式

  3. 函数指针(但不推荐,性能差)

优先级队列基础知识:C++ 容器类 <priority_queue> | 菜鸟教程

Bellman_ford 算法精讲

卡码网:94. 城市间货物运输 I

Bellman_ford 算法解决边有负权值的情况

对所有边松弛一次,相当于计算 起点到达 与起点一条边相连的节点 的最短距离(代码随想录)需要对所有边松弛n-1次才能得到 起点(节点1) 到终点(节点n)的最短距离

松弛:理解上来说,松弛就是更新minDis数组的过程,对A到B这条边

if (minDist[B] > minDist[A] + value) minDist[B] = minDist[A] + value

 在判断条件中要加上判断该节点minDist数组是否被计算过,边的起点的minDist没被更新时不能更新边的终点

minDist[from] != INT_MAX

#include <iostream>
#include <vector>
#include <climits>
using namespace std;
 
int main(){
    int n,m,s,t,v;
    cin>>n>>m;
    vector<vector<int>> traffics;
    for(int i=0;i<m;i++){
        cin>>s>>t>>v;
        traffics.push_back({s,t,v});
    }
    int start=1;
    int end=n;
 
    vector<int> transCost(n+1,INT_MAX);
    transCost[1]=0;
 
    for(int i=0;i<n-1;i++){              //松弛次数为点数-1,而不是边数m-1
        for(vector<int>& side : traffics){
            int from=side[0];
            int to = side[1];
            int price = side[2];
            if(transCost[from]!=INT_MAX&&transCost[to]>transCost[from]+price){
                transCost[to]=transCost[from]+price;
            }
        }
    }    
    if(transCost[end]==INT_MAX) cout<<"unconnected";
    else cout<<transCost[end];
}
  • 时间复杂度: O(N * E) , N为节点数量,E为图中边的数量
  • 空间复杂度: O(N) ,即 minDist 数组所开辟的空间

题目说保证道路网络中不存在任何负权回路,对于负权回路使用该写法的话,比如下面这种情况:

1 → 2 → 3 → 4 → 2

设这个回路 2 → 3 → 4 → 2 的权值总和是负数,比如 -10

那么你可以无限次从城市1绕一圈这个回路,每次都让总花费减 10,理论上可以让路径成本趋近于负无穷!

这种情况下,没有最短路径这个概念了,因为你永远可以更短(或者说赚更多)。

对于朴素dijkstra,都是正数的情况下对于图中(图源代码随想录)这种,

个人小疑点:如果-300处是正数的话,就能正确了吗?答案是当然,可以随便更改1到2,1到3,2到3三个位置处的权值,取一个极端情况,1到2和1到3都是1,但第一步选的1到3,因为是正数,所以1到2的1随便加一个正数都比1到3大了,所以不会出现错过1到2到3权值更小但被错过的情况

http://www.dtcms.com/a/112065.html

相关文章:

  • Docker安装、配置Redis
  • 使用Expo框架开发APP——详细教程
  • 【JavaScript】原型链 prototype 和 this 关键字的练习(老虎机)
  • 安当TDE透明加密:构建跨地域文件服务器安全传输的“双重保险“
  • VBA中类的解读及应用第二十二讲:利用类判断任意单元格的类型-5
  • C语言:3.31
  • 【YOLO系列(V5-V12)通用数据集-火灾烟雾检测数据集】
  • 大模型学习四:‌DeepSeek Janus-Pro 多模态理解和生成模型 本地部署指南(折腾版)
  • 七均线策略思路
  • Mac VM 卸载 win10 安装win7系统
  • Win7下安装高版本node.js 16.3.0 以及webpack插件的构建
  • Apache Camel指南-第四章:路由径构建之异常处理
  • 如何使用 IntelliJ IDEA 开发命令行程序(或 Swing 程序)并手动管理依赖(不使用 pom.xml)
  • 从飞机的状态矩阵A阵求运动模态的特征根、求传递函数矩阵
  • NOIP2013提高组.华容道
  • 从菜鸟到高手的提示词优化指南‌
  • Muduo网络库介绍
  • Sensodrive力控关节模组SensoJoint:TÜV安全认证助力机器人开发
  • 主机和虚拟机间的网络通信
  • LeetCode算法题(Go语言实现)_29
  • JavaScript重难点突破:事件循环
  • 基于 Python 的自然语言处理系列(70):检索增强生成(RAG)
  • Go语言-初学者日记(八):构建、部署与 Docker 化
  • 《操作系统真象还原》第五章(2)——启用内存分页机制
  • 蓝桥杯15届 宝石组合
  • 【HC-05蓝牙模块】基础AT指令测试
  • 思维链 Chain-of-Thought(COT)
  • 视野,,地面覆盖,重叠需求,FPS,飞行速度等的计算公式
  • LLM面试题五
  • JVM 有哪些垃圾回收器