给你一个二维整数数组
orders
,其中每个orders[i] = [pricei, amounti, orderTypei]
表示有amounti
笔类型为orderTypei
、价格为pricei
的订单。订单类型
orderTypei
可以分为两种:
0
表示这是一批采购订单buy
1
表示这是一批销售订单sell
注意,
orders[i]
表示一批共计amounti
笔的独立订单,这些订单的价格和类型相同。对于所有有效的i
,由orders[i]
表示的所有订单提交时间均早于orders[i+1]
表示的所有订单。存在由未执行订单组成的 积压订单 。积压订单最初是空的。提交订单时,会发生以下情况:
- 如果该订单是一笔采购订单
buy
,则可以查看积压订单中价格 最低 的销售订单sell
。如果该销售订单sell
的价格 低于或等于 当前采购订单buy
的价格,则匹配并执行这两笔订单,并将销售订单sell
从积压订单中删除。否则,采购订单buy
将会添加到积压订单中。- 反之亦然,如果该订单是一笔销售订单
sell
,则可以查看积压订单中价格 最高 的采购订单buy
。如果该采购订单buy
的价格 高于或等于 当前销售订单sell
的价格,则匹配并执行这两笔订单,并将采购订单buy
从积压订单中删除。否则,销售订单sell
将会添加到积压订单中。输入所有订单后,返回积压订单中的 订单总数 。由于数字可能很大,所以需要返回对
109 + 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 。
/**
* @param {number[][]} orders
* @return {number}
*/
var getNumberOfBacklogOrders = function(orders) {
let mod = 1000000007
let buy = new MaxPriorityQueue({ priority: (bid) => bid.price })
let sell = new MinPriorityQueue({ priority: (bid) => bid.price })
let total = 0
for(let [price, amount, orderType] of orders) {
if (orderType === 0) { // 买 找卖小
while (!sell.isEmpty() && sell.front().priority <= price && amount > 0) {
let head = sell.dequeue().element
if (amount < head.amount) {
sell.enqueue({price: head.price, amount: head.amount - amount})
total -= amount
amount = 0
} else {
amount -= head.amount
total -= head.amount
}
}
if (amount > 0) buy.enqueue({price, amount}), total += amount
} else {// 卖 找买大
while (!buy.isEmpty() && buy.front().priority >= price && amount > 0) {
let head = buy.dequeue().element
if (amount < head.amount) {
buy.enqueue({price: head.price, amount: head.amount - amount})
total -= amount
amount = 0
} else {
amount -= head.amount
total -= head.amount
}
}
if (amount > 0) sell.enqueue({price, amount}), total += amount
}
}
return total % mod
};
代码解读:使用了两个优先队列:
buy
和sell
。buy
队列用于存储购买订单,按价格降序排列;sell
队列用于存储出售订单,按价格升序排列。这样可以快速找到匹配的订单。接下来,我们逐行分析代码:
let mod = 1000000007
:定义一个模数,用于计算结果时取模,防止整数溢出。let buy = new MaxPriorityQueue({ priority: (bid) => bid.price })
:创建一个最大优先级队列buy
,用于存储购买订单,按价格降序排列。let sell = new MinPriorityQueue({ priority: (bid) => bid.price })
:创建一个最小优先级队列sell
,用于存储出售订单,按价格升序排列。let total = 0
:初始化总订单数量为0。 5-26. 遍历orders
数组中的每个订单:
- 如果订单类型为0(购买),则尝试与
sell
队列中的价格较低的出售订单匹配。如果匹配成功,更新剩余数量和总订单数量。如果没有完全匹配,将剩余的购买订单加入buy
队列。- 如果订单类型为1(出售),则尝试与
buy
队列中的价格较高的购买订单匹配。如果匹配成功,更新剩余数量和总订单数量。如果没有完全匹配,将剩余的出售订单加入sell
队列。return total % mod
:返回总订单数量对模数取模的结果。这个算法使用了贪心策略来处理订单匹配。通过维护两个优先队列,我们可以在O(log n)的时间复杂度内找到合适的匹配订单,从而大大提高了效率。
学习补充:
最大优先队列用于获取并删除队列中具有最大关键字的元素,而最小优先队列则用于获取并删除具有最小关键字的元素。
小顶堆是升序,找最小值;
大顶堆是降序,找最大;
最大优先队列和大顶堆 最小优先队列和小顶堆的关系:
最大优先队列,是降序,删除最大值,-------大顶堆是降序,找最大;
最小优先队列是升序,删除最小值;------小顶堆是升序,找最小值;