给你一个二维整数数组 orders ,其中每个 orders[i] = [pricei, amounti, orderTypei] 表示有 amounti 笔类型为 orderTypei 、价格为 pricei 的订单。
订单类型 orderTypei 可以分为两种:
0表示这是一批采购订单buy1表示这是一批销售订单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 。
示例 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) 。
这道题乍一看很唬人,让人完全看不懂的样子,咱们一起分析一下这道题。首先我们能确定,这道题需要两个存储空间,一个buy采购订单,一个sell销售订单。他们分别用orders的第三个参数orderType代表0为采购,1为销售。
orders[i] 的第一个参数为价格,第二个参数为价格相同的订单数量。
由
orders[i]表示的所有订单提交时间均早于orders[i+1]表示的所有订单。
表示orders的订单是按顺序发放的,我们需要按顺序遍历。我们需要计算积压的订单数total,并没有说是销售单还是采购单,所以按总量计算。
如果该订单是一笔采购订单
buy,则可以查看积压订单中价格 最低 的销售订单sell。
那么说明如果该订单的type为0,则需要在sell中找到最小值。
如果该订单是一笔销售订单
sell,则可以查看积压订单中价格 最高 的采购订单buy。
那么说明如果该订单的type为1,则需要在buy中找到最大值。
综上我们可以断定需要用到大小顶堆。sell为小顶堆,buy为大顶堆。
我们先创建一个堆函数:
class Heap {
constructor(cmp = "large") {
if (cmp == "large") {
this.cmp = this.large;
} else if (cmp == "small") {
this.cmp = this.small
} else {
this.cmp = cmp
}
this.res = [];
this.cnt = 0;
}
push (val) {
this.cnt++;
this.res.push(val)
this.shiftUp(this.cnt - 1)
}
pop () {
this.cnt--;
const res = this.res[0]
const pop = this.res.pop()
if (this.cnt) {
this.res[0] = pop
this.shiftDown(0)
}
return res
}
shiftUp (i) {
if (i === 0) return
const par = this.getParentIndex(i)
if (this.cmp(this.res[par], this.res[i])) {
this.swap(par, i)
this.shiftUp(par)
}
}
shiftDown (i) {
const l = this.getLeftIndex(i)
const r = this.getRightIndex(i)
if (l < this.cnt && this.cmp(this.res[i], this.res[l])) {
this.swap(i, l)
this.shiftDown(l)
}
if (r < this.cnt && this.cmp(this.res[i], this.res[r])) {
this.swap(i, r)
this.shiftDown(r)
}
}
getParentIndex (i) {
return (i - 1) >> 1
}
getLeftIndex (i) {
return i * 2 + 1
}
getRightIndex (i) {
return i * 2 + 2
}
large = (a, b) => a < b
small = (a, b) => a > b;
swap = (i, j) => [this.res[i], this.res[j]] = [this.res[j], this.res[i]];
top = () => this.res[0];
size = () => this.cnt;
isEmpty = () => this.cnt === 0
}
我们首先遍历orders,创建大小顶堆,并添加数据
const buy = new Heap(( a,b ) => a.price < b.prcie)
const sell = new Heap(( a,b ) => a.price > b.prcie)
for(let i = 0; i<orders.length; i++){
let [price, amount, orderType] = orders[i]
if(orderType==0){
buy.push({price, amount})
}else{
sell.push({price, amount})
}
}
此时我们就完成了第一步,大小顶堆的创建和填值。下面我们就要进行比较了。
由上面可知,如果是销售订单,我们需要查看采购订单的堆顶。如果是采购订单,我们需要看销售订单的堆顶。
如果该销售订单
sell的价格 低于或等于 当前采购订单buy的价格,则匹配并执行这两笔订单,并将销售订单sell从积压订单中删除。否则,采购订单buy将会添加到积压订单中。
也就是说,我们我发现订单是销售订单我们不能马上入堆,还需要进行比较,如果buy的堆顶大于该订单,我们需要进行抵消,同时抵消时不能完全保证两者数量相同,所以我们需要先取出buy的堆顶,进行数量抵消,剩下的那个再入堆。
else { // orderType == 1时
// 判断采购订单是否存在,并且堆顶元素的售价要大于等于当前销售订单的单价
while (!buy.isEmpty() && buy.top().price >= price && amount > 0) {
let top = buy.pop()
// 如果采购订单数更多,则采购订单数、总订单数减少当前销售订单数的数量
if (amount < top.amount) {
buy.push({ price: top.price, amount: top.amount - amount })
total -= amount
amount = 0
} else { // 相反采购订单全部被抵消,计算剩余的销售订单和总订单数
amount -= top.amount
total -= top.amount
}
}
// 如果销售订单还有剩余,入堆。积压总订单数增加。
if (amount > 0) {
sellOrder.push({ price, amount })
total += amount
}
}
同理,当遇到时采购订单时,需要查看销售订单的堆顶。
如果该采购订单
buy的价格 高于或等于 当前销售订单sell的价格,则匹配并执行这两笔订单,并将采购订单buy从积压订单中删除。否则,销售订单sell将会添加到积压订单中。
if (orderType == 0) {
while (!sell.isEmpty() && sell.top().price <= price && amount > 0) {
let top = sellOrder.pop();
if (amount < top.amount) {
sell.push({ price: top.price, amount: top.amount - amount })
total -= amount
amount = 0
} else {
amount -= top.amount;
total -= top.amount
}
}
if (amount > 0) {
buy.push({ price, amount })
total += amount
}
}
最后由于数量可能过大,所以需要对结果进行取余
const mod = 1000000007
return total % mod
总结起来就是
-
对buy和sell分别创建大小顶堆。
-
判断orders[i]的订单类型
-
判断订单,查看对应堆顶,如果符合条件,进行数量抵消,重新添加剩余数量
var getNumberOfBacklogOrders = function (orders) { const mod = 1000000007 const buyOrder = new Heap((a, b) => a.price < b.price); const sellOrder = new Heap((a, b) => a.price > b.price); let total = 0; for (let i = 0; i < orders.length; i++) { let [price, amount, orderType] = orders[i] if (orderType == 0) { while (!sellOrder.isEmpty() && sellOrder.top().price <= price && amount > 0) { let sell = sellOrder.pop(); if (amount < sell.amount) { sellOrder.push({ price: sell.price, amount: sell.amount - amount }) total -= amount amount = 0 } else { amount -= sell.amount; total -= sell.amount } } if (amount > 0) { buyOrder.push({ price, amount }) total += amount } } else { while (!buyOrder.isEmpty() && buyOrder.top().price >= price && amount > 0) { let buy = buyOrder.pop() if (amount < buy.amount) { buyOrder.push({ price: buy.price, amount: buy.amount - amount }) total -= amount amount = 0 } else { amount -= buy.amount total -= buy.amount } } if (amount > 0) { sellOrder.push({ price, amount }) total += amount } } } console.log(total % mod); return total % mod };