【LeetCode每日一题】【2023/1/2】1801. 积压订单中的订单总数

news2025/1/4 17:38:33

文章目录

  • 1801. 积压订单中的订单总数
    • 方法1:模拟+优先队列
      • part1
      • priority_queue的使用
      • part2
      • 求余
      • 代码


1801. 积压订单中的订单总数

LeetCode: 1801. 积压订单中的订单总数

中等 \color{#FFB800}{中等} 中等

给你一个二维整数数组 orders ,其中每个 orders[i] = [price_i, amount_i, orderType_i] 表示有
amount_i 笔类型为 orderType_i 、价格为 price_i 的订单。

订单类型 orderType_i 可以分为两种:

  • 0 表示这是一批采购订单 buy
  • 1 表示这是一批销售订单 sell

注意,orders[i] 表示一批共计 amount_i 笔的独立订单,这些订单的价格和类型相同。对于所有有效的 i ,由 orders[i] 表示的所有订单提交时间均早于 orders[i+1] 表示的所有订单。

存在由未执行订单组成的 积压订单 。积压订单最初是空的。提交订单时,会发生以下情况:

  • 如果该订单是一笔采购订单 buy ,则可以查看积压订单中价格 最低 的销售订单 sell 。如果该销售订单 sell 的价格 低于或等于 当前采购订单 buy 的价格,则匹配并执行这两笔订单,并将销售订单 sell 从积压订单中删除。否则,采购订单 buy 将会添加到积压订单中。
  • 反之亦然,如果该订单是一笔销售订单 sell ,则可以查看积压订单中价格 最高 的采购订单 buy 。如果该采购订单 buy 的价格 高于或等于 当前销售订单 sell 的价格,则匹配并执行这两笔订单,并将采购订单 buy 从积压订单中删除。否则,销售订单 sell 将会添加到积压订单中。

输入所有订单后,返回积压订单中的 订单总数 。由于数字可能很大,所以需要返回对 10^9 + 7 取余的结果。

示例 1:
在这里插入图片描述

输入:orders = [[10,5,0],[15,2,1],[25,1,1],[30,4,0]]
输出:6
解释:输入订单后会发生下述情况:
- 提交 5 笔采购订单,价格为 10 。没有销售订单,所以这 5 笔订单添加到积压订单中。
- 提交 2 笔销售订单,价格为 15 。没有采购订单的价格大于或等于 15 ,所以这 2 笔订单添加到积压订单中。
- 提交 1 笔销售订单,价格为 25 。没有采购订单的价格大于或等于 25 ,所以这 1 笔订单添加到积压订单中。
- 提交 4 笔采购订单,价格为 30 。前 2 笔采购订单与价格最低(价格为 15)的 2 笔销售订单匹配,从积压订单中删除这 2 笔销售订单。第 3 笔采购订单与价格最低的 1 笔销售订单匹配,销售订单价格为 25 ,从积压订单中删除这 1 笔销售订单。积压订单中不存在更多销售订单,所以第 4 笔采购订单需要添加到积压订单中。
最终,积压订单中有 5 笔价格为 10 的采购订单,和 1 笔价格为 30 的采购订单。所以积压订单中的订单总数为 6

示例 2:
在这里插入图片描述

输入:orders = [[7,1000000000,1],[15,3,0],[5,999999995,0],[5,1,1]]
输出:999999984
解释:输入订单后会发生下述情况:
- 提交 109 笔销售订单,价格为 7 。没有采购订单,所以这 109 笔订单添加到积压订单中。
- 提交 3 笔采购订单,价格为 15 。这些采购订单与价格最低(价格为 7 )的 3 笔销售订单匹配,从积压订单中删除这 3 笔销售订单。
- 提交 999999995 笔采购订单,价格为 5 。销售订单的最低价为 7 ,所以这 999999995 笔订单添加到积压订单中。
- 提交 1 笔销售订单,价格为 5 。这笔销售订单与价格最高(价格为 5 )的 1 笔采购订单匹配,从积压订单中删除这 1 笔采购订单。
最终,积压订单中有 (1000000000-3) 笔价格为 7 的销售订单,和 (999999995-1) 笔价格为 5 的采购订单。所以积压订单中的订单总数为 1999999991 ,等于 999999984 % (109 + 7)

提示:

  • 1 <= orders.length <= 10^5
  • orders[i].length == 3
  • 1 <= price_i, amount_i <= 10^9
  • orderType_i01

方法1:模拟+优先队列

part1

按题意:如果是 buy 订单,我们要寻找 价格 最低的 sell 订单进行操作;如果是 sell 订单,我们要寻找 价格 最高的 buy 订单进行操作。若满足相应的条件,则可以视为当前订单与目标订单 “抵消” 。

所以我们需要对两种订单进行排序。由于题目要求订单 order[i] 的出现时间比 order[i+1] 早,因此我们需要在算法中,按序处理订单。这里就需要用到 模拟 的方法。同时,sell 序列和 buy 序列中的元素会随着订单的出现和变化,因此我们选择使用 优先队列)来排序。

对于 模拟 的过程,我们只需要创建一个循环,在循环中遍历订单即可。对于 排序 ,要寻找 价格 最低的 sell 订单则 sellBacking 是一个 小根堆 ;要寻找 价格 最高的 buy 订单则 buyBacking 是一个 大根堆 。C++的标准库中,在头文件 queue 中给出了优先队列(堆)这个数据结构 priority_queue 。其默认为大根堆。

有了数据结构之后,我们应思考这个堆里面元素的类型。我们首先也许会将 价格 作为一个整型存放到堆里面,毕竟 按价格排序 。但是题目中给出要求 1 <= amount_i <= 10^9 ,也就是说一批订单 order[i] 内最高可达到 10的9次方 笔订单。这就意味这我们会在堆中压入 109 个整型,又要循环 109 次来处理 一批 订单。这就一来,一批 订单就要需要消耗 109 * sizeof(int) 约为 3.7GB 内存空间,更何况订单最多有 105 批(1 <= orders.length <= 10^5)。所以这个简单的想法是不现实的。

我们要把 价格数量 组合为一个二元组 std::pair 存入堆中,即堆的元素类型为 std::pair<int, int> (题目给出的函数定义中,价格和数量即为 int )。


priority_queue的使用

(优先队列,或者说小根堆、大根堆,若不使用 priority_queue 实现,可以跳过)

在C++中,我们在定义 优先队列 变量的时候,若 元素类型复杂元素间比较方式需自定义,则需要给出排序的函数,告诉这个堆怎么去进行两个元素的比较从而确定它们的先后关系。因为我们只需要对 价格 进行排序,而 数量 不作为排序的依据。如 sell 订单的 比较函数 如下:

// pair<price, amount>
auto sellCompare = [](
    const std::pair<int, int> &left,
    const std::pair<int, int> &right
)
{ return left.first > right.first; };

在优先队列对元素进行排序的时候,每次会拿出两个元素,调用这个函数来确定先后关系。同时,上述代码在定义 二元组 的时候,将 价格 放在了二元组的第一个元素处,因此我们实现了对 leftrightfirst 成员进行比较。

在cppreference上,我们可以查看 priority_queue 的详细内容。有句话是这么告诉我们的:

可用用户提供的 Compare 更改顺序,例如,用 std::greater 将导致最小元素作为 top() 出现。

Compare 即为我们提供的 比较函数 ,例如上面代码里的 sellCompare 函数。std::greater 可以理解为这个函数的比较方式是: “左边”的元素 left “大于” “右边”的元素 right,例如上面代码里的返回语句 left.first > right.first。当 “左边大于右边” 这个条件成立(比较函数 返回 true)时,此时这个堆是一个 小根堆

这样一来,我们提供了 sellBacking 这个堆的比较函数,通过这个比较函数,使得了这个堆成为了 小根堆。同时我们也告诉了这个堆怎么比较元素,那就是比较两边的 first

接下来我们就正式去定以两个堆变量 sellBackingbuyBacking 。在cppreference上给出了 priority_queue 的模板参数:

template<
    class T,
    class Container = std::vector<T>,
    class Compare = std::less<typename Container::value_type>
> class priority_queue;

首先,第一个模板参数是这个优先队列的元素类型 T ,在本题中即为二元组 std::pair<int, int>

如果元素类型 T 是一般的类型,比如说 floatint 之类,那么后面两个参数就不用写明。但是我们这里的元素类型是一个比较复杂的 二元组 ,并且元素的比较方式也是自己定义(比较 first 成员)。这两个因素都导致我们需要提供自己的比较函数,继而需要写明后面两个模板参数。第二个参数是用来存放这些元素的容器类型,我们可以使用 std::vector 数组,写为 std::vector<std::pair<int, int> > 。第三个参数是比较函数它本身的类型,我们可以用 decltype 函数得到这个类型,写为 decltype(sellCompare)

最后,在构造函数中传入这个比较函数作为参数即可。

关于小根堆 sellBacking 和大根堆 buyBacking 的定义如下:

// pair<price, amount>
typedef std::pair<int, int> Record;

auto sellCompare = [](const Record &left, const Record &right)
{ return left.first > right.first; };
priority_queue<Record, vector<Record>, decltype(sellCompare)> sellBacking{sellCompare};

auto buyCompare = [](const Record &left, const Record &right)
{ return left.first < right.first; };
priority_queue<Record, vector<Record>, decltype(buyCompare)> buyBacking{buyCompare};
  • 通过比较函数 sellCompare左边“大于”右边 的写法,使得 sellBacking 为小根堆;
  • 通过比较函数 buyCompare左边“小于”右边 的写法,使得 buyBacking 为大根堆。

part2

sellBacking 为小根堆,buyBacking 为大根堆)

buy 订单为例,每次接受到一批存放在 order[i] 里的 buy 订单,我们要去访问 sellBacking 的堆顶。接下来会有以下情况出现:

  1. 不满足条件(sell 的价格 低于或等于 当前采购订单 buy 的价格),将 buy 订单压入 buyBacking
    • 满足条件,但 buy 订单 不够 抵消完 堆顶的 sell 订单。此时需要更改 sellBacking 堆的堆顶元素,将 sell 订单的数量减去已经抵消的数量。(priority_queue 不能直接修改堆顶,需要弹出、修改、再压入)。
    • buy 订单 足够 抵消完 堆顶的 sell 订单。此时需要弹出堆顶。若 buy 订单还有剩余,那么还需要对弹出后的 新堆顶 进行 再次匹配
  2. 无论如何到了最后,若 buy 订单的数量依旧有剩余,则压入 buyBacking

同理,sell 订单的处理过程与上述基本相同。


求余

对于数 abmod(a + b) % mod == (a % mod + b % mod) % mod

即 “和的余数” 等于 “余数的和再求余”


代码

最后的代码如下:

#include <vector>
#include <queue>
using namespace std;

class Solution
{
public:
    // pair<price, amount>
    typedef std::pair<int, int> Record;

    int getNumberOfBacklogOrders(vector<vector<int>> &orders)
    {

        auto sellCompare = [](const Record &left, const Record &right)
        { return left.first > right.first; };
        priority_queue<Record, vector<Record>, decltype(sellCompare)> sellBacking{sellCompare};

        auto buyCompare = [](const Record &left, const Record &right)
        { return left.first < right.first; };
        priority_queue<Record, vector<Record>, decltype(buyCompare)> buyBacking{buyCompare};

        for (const vector<int> &order : orders)
        {
            const int &price = order[0];
            int amount = order[1];
            const int &orderType = order[2];

            if (orderType == 0)
            { // buy order
                int sellPrice = 0, sellAmount = 0;

                while (!sellBacking.empty() && sellBacking.top().first <= price && amount > 0)
                {
                    sellPrice = sellBacking.top().first;
                    sellAmount = sellBacking.top().second;
                    sellBacking.pop();
                    amount -= sellAmount;
                }

                if (amount > 0)
                {
                    buyBacking.emplace(price, amount);
                }
                else
                {
                    sellAmount = std::abs(amount);
                    sellBacking.emplace(sellPrice, sellAmount);
                }
            }
            else
            { // sell order
                int buyPrice = 0, buyAmount = 0;

                while (!buyBacking.empty() && buyBacking.top().first >= price && amount > 0)
                {
                    buyPrice = buyBacking.top().first;
                    buyAmount = buyBacking.top().second;
                    buyBacking.pop();
                    amount -= buyAmount;
                }

                if (amount > 0)
                {
                    sellBacking.emplace(price, amount);
                }
                else
                {
                    buyAmount = std::abs(amount);
                    buyBacking.emplace(buyPrice, buyAmount);
                }
            }
        }

        constexpr int mod = 1000000007;
        int ans = 0;
        for (; !sellBacking.empty(); sellBacking.pop())
            ans = (ans + sellBacking.top().second) % mod;
        for (; !buyBacking.empty(); buyBacking.pop())
            ans = (ans + buyBacking.top().second) % mod;
        return ans;
    }
};

复杂度分析:

  • 时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)。其中,n 为数组 orders 的长度。首先需要遍历 orders 数组($O(n));其次在遍历过程中,对优先队列(堆)的每次压入、弹出操作均为 O ( l o g n ) O(logn) O(logn)。总的来说,时间复杂度为两者乘积 O ( n l o g n ) O(nlogn) O(nlogn)

  • 空间复杂度: O ( n ) O(n) O(n)。优先队列需要的 O ( n ) O(n) O(n)的空间。

参考结果

Accepted
69/69 cases passed (188 ms)
Your runtime beats 87.63 % of cpp submissions
Your memory usage beats 55.67 % of cpp submissions (5.9 MB)

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

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

相关文章

基于springboot+Vue学生宿舍管理系统(程序+数据库+文档+代码解读)

大家好✌&#xff01;我是CZ淡陌。一名专注以理论为基础实战为主的技术博主&#xff0c;将再这里为大家分享优质的实战项目&#xff0c;本人在Java毕业设计领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目&#xff0c;希望你能有所收获&#xff0c;少走一些弯路…

使用Cramer-Rao和Athley边界分析到达角阵列的质量(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 此代码用于使用Cramer-Rao和Athley边界分析到达角阵列的质量。 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 [1]王凯欣…

感谢、感恩与CSDN结缘的第1498天

导读&#xff1a;2023年1月1日&#xff0c;这是本人加入CSDN的1498天&#xff0c;还记得2018-11-26是注册加入CSDN的第一天&#xff0c;当时的初心就是单纯想作为一名普通的IT人&#xff0c;随着不断打拼职场岁月流逝&#xff0c;总想沉淀留下点东西&#xff0c;即使再平凡也总…

【数据集8】全球人口数据WorldPop详解

全球人口数据WorldPop WorldPop是由南安普顿大学在2013年10月发起的全球人口数据评估。与LandScan数据相比&#xff0c;这个数据涵盖的数据集的社会经济属性更多一些&#xff0c;比如包括了年龄性别结构、出生率、人口流动、航班联系等数据&#xff0c;可参见下图。 官网地址-…

网络信息安全-图像隐写与检测综述

任务目标&#xff1a; 本选题需要学习经典的图像信息隐藏算法&#xff0c;包括基于空域的隐写算法和数字水印算法。 接着你将使用某种编程语言实现这些算法&#xff0c;实现在图片中嵌入一些信息&#xff0c;例如字符串和一些 文件。除此之外&#xff0c;还需要尝试一些基础的…

MySQL8--通过角色管理权限

原文网址&#xff1a;MySQL8--通过角色管理权限_IT利刃出鞘的博客-CSDN博客 简介 本文介绍MySQL8如何通过角色管理权限。 角色是在MySQL8.0中引入的新功能。在MySQL中&#xff0c;角色是权限的集合&#xff0c;可以为角色添加或移除权限。用户可以被赋予角色&#xff0c;同时也…

洛普兰机械密封盛装亮相2023第11届济南生物发酵展

洛普兰机械密封与您相约2023第11届济南生物发酵展览会&#xff0c;3月30-4月1号山东国际会展中心&#xff0c;诚邀参会&#xff01; 展位号&#xff1a;3号馆H32 2023生物发酵展将于3月30号山东国际会展中心&#xff08;济南市槐荫区日照路1号&#xff09;盛大召开&#xff0…

Spring(四):Bean作用域和生命周期

目录一、Bean作用域作用域1.1 Bean作用域简介1.2 作用域的定义1.3 Bean的6种作用域1.4 设置作用域二、Spring的执行流程和Bean的生命周期2.1 Spring的执行流程2.2 Bean的生命周期一、Bean作用域作用域 1.1 Bean作用域简介 现在有一个公共的Bean&#xff0c;A用户先使用到这个…

统计学中的Bootstrap方法(Bootstrap抽样)用来训练bagging算法,如果随机森林Random Forests

统计学中的Bootstrap方法&#xff08;Bootstrap抽样&#xff09;用来训练bagging算法&#xff0c;如果随机森林Random Forests 提示&#xff1a;系列被面试官问的问题&#xff0c;我自己当时不会&#xff0c;所以下来自己复盘一下&#xff0c;认真学习和总结&#xff0c;以应对…

Java与Python常见语法对比及区别

这里写目录标题一、前记1.1为什么写这篇文章&#xff1f;1.2 本文的结构二、整体区别2.1 差异总结十句话2.2整体对比三、细节区别3.1数据类型3.2其他数据结构3.3 String的处理方法3.4数组3.5条件语句3.6循环语句3.7类和接口定义与调用四 其他技巧知识字节的换算Tips&#xff1a…

flutter项目编译问题汇总

1、kotlin compiler embeddable下载慢的解决办法 备注&#xff1a;可以使用https://developer.aliyun.com/mvn/search这个链接搜索并下载 2、运行Flutter项目一直卡在--Running Gradle task assembleDebug 解决方法&#xff1a; 第一步&#xff1a;修改flutter安装目录/pac…

校招前端一面经典react面试题(附答案)

React.forwardRef是什么&#xff1f;它有什么作用&#xff1f; React.forwardRef 会创建一个React组件&#xff0c;这个组件能够将其接受的 ref 属性转发到其组件树下的另一个组件中。这种技术并不常见&#xff0c;但在以下两种场景中特别有用&#xff1a; 转发 refs 到 DOM …

多层次目录结构的CMake工程管理

多层次目录结构的CMake工程管理一、多层次目录结构的文件结构二、如何利用CMake组织多层次目录结构三、构建工程一、多层次目录结构的文件结构 我们编写程序&#xff0c;不可能把所有源文件都一股脑地放在顶层目录下&#xff0c;必然会有一个目录结构&#xff0c;每个目录中只…

【django】项目开发准备之数据库配置

文章目录一、docker中启动mariadb服务二、django配置文件中进行配置1.安装mysqlclient2.找到settings.py文件3.启动django程序一、docker中启动mariadb服务 --restartalways:自动启动容器 --name mydb&#xff1a;容器名称 -d&#xff1a;后台运行容器 -v /home/ubuntu/ck14/ma…

php宝塔搭建部署实战服务类家政钟点工保姆网站源码

大家好啊&#xff0c;我是测评君&#xff0c;欢迎来到web测评。 本期给大家带来一套php开发的服务类家政钟点工保姆网站源码&#xff0c;感兴趣的朋友可以自行下载学习。 技术架构 PHP7.2 nginx mysql5.7 JS CSS HTMLcnetos7以上 宝塔面板 文字搭建教程 下载源码&…

单机模拟搭建 Zookeeper 集群

1. 准备 官网下载&#xff1a;https://www.apache.org/dyn/closer.lua/zookeeper/zookeeper-3.6.3/apache-zookeeper-3.6.3-bin.tar.gz 注意&#xff1a; zookeeper 从 3.5 版本以后&#xff0c;命名就发生了改变apache-zookeeper-X.X.X.tar.gz 这样命名的&#xff0c;都是未…

并发编程之JMMvolatile详解

目录 什么是JMM模型 定义 JMM不同于JVM内存区域模型 主内存 工作内存 Java内存模型与硬件内存架构的关系 JMM存在的必要性 数据同步八大原子操作 同步规则分析 并发编程的可见性&#xff0c;原子性与有序性问题 原子性 可见性 有序性 JMM如何解决原子性&…

【单片机】AT24C02存储器(I²C总线)/DS18B20温度传感器(单总线)

目录 一、AT24C02存储器 1、AT24C02存储器介绍 2、存储器简化模型 3、AT24C02存储器原理图 二、IC总线 1、IC总线的介绍 2、IC电路 3、IC时序图 3.1IC开始和结束时序图 3.2IC主机发送和接收时序图 3.3IC主机发送应答/接收应答 4、IC数据帧发送/接收方式 4.1主机向…

数据可视化系列-02各类图表的综合使用介绍及实践-上篇

文章目录3.各类图表的综合使用介绍及实践3.1了解数据功能图1、可视化中的数据2、基于数据的研究3、数据的可视化组件4、可视化图表的作用、制作流程和类型3.2北极星指标展示1、指标类知识回顾&#xff1a;指标类简介、指标类主要场景2、指标类图表&#xff1a;指标看板、指标趋…

C++关联容器(复习题篇)

本篇博客将介绍标准库关联容器&#xff0c;包括&#xff1a; 关联容器的概念和简单的使用关联容器涉及的类型和操作&#xff0c;特别是与顺序容器的差异无序关联容器&#xff0c;特别是与有序关联容器的差异 练习1.1 描述map和vector的不同 vector是顺序容器其中的元素是“顺序…