[路飞]_积压订单中的订单总数

407 阅读5分钟

「这是我参与2022首次更文挑战的第10天,活动详情查看:2022首次更文挑战

leetcode-1801 积压订单中的订单总数

题目介绍

原题目比较长,描述也是晦涩难懂,我用大白话对题目做个介绍(需要看原题的请戳上方链接)

  1. 题目会传进来一个二维数组,数组的结构是这样的 [[price1, amount1, orderType1], [price2, amount2, orderType2] ... ]
  • price: 表示订单的价格
  • orderType: 表示订单类型 (0:采购订单, 1:销售订单)
  • amount: 表示价格和类型都相同的订单的数量
  1. 题目规则
  • 如果一批订单是购买订单,则看 销售积压订单 中是否存在 价格小于等于购买订单价格 的订单,如果存在,则删除 购买订单 或者 销售积压订单中小于等于购买订单价格的订单较小值,购买订单多出的订单数加入到 购买积压订单
  • 如果一批订单是销售订单,则看 购买积压订单 中是否存在 价格大于等于销售订单价格 的订单,如果存在,则删除 销售订单 或者 购买积压订单中大于等于销售订单价格的订单较小值,销售订单多出的订单数加入到 销售积压订单
  • 最后返回 销售积压订单购买积压订单 的订单总数对 10^9 + 7 取余的结果

示例1

image.png

示例2

image.png

解题思路

此题关键是要理解订单之间抵消的规则,购买订单是从销售积压订单中的最小值开始比较,因此可以用一个小顶堆维护 销售积压订单;销售订单是从购买订单中的最大值开始比较,因此用一个大顶堆维护 购买积压订单

在此题中,订单的总数可能很大,因此不能将每一个订单都放到堆中,这样会导致内存溢出,最好的做法是以 {price: '订单价格', amount: '订单数量'} 来进行存储,每次抵消堆顶元素一定数量的订单,再重新将剩余的积压订单插入堆中

解题步骤

  1. 创建一个大顶堆 buy 存放 购买积压订单,创建一个小顶堆 sell 存放 销售积压订单,变量 total 实时保存当前积压的订单总数
  2. 遍历订单列表:
  • 如果当前订单是购买订单,则看 销售积压订单 中是否有订单
    • 如果没有,将当前这批购买订单的 {price: '订单价格', amount: '订单数量'} 插入 购买积压订单buy 中,total 加上这批购买订单的数量
    • 如果有,弹出 销售积压订单sell 的堆顶元素
      • 如果堆顶订单的数量 大于 购买订单的数量,将堆顶订单的数量减去购买订单的数量,然后重新插入到 销售积压订单sell 中,total 减去这批购买订单的数量
      • 如果堆顶订单的数量 小于 购买订单的数量,将购买订单的数量减去堆顶订单的数量,total 减去堆顶订单的数量,继续弹出 销售积压订单sell 的堆顶元素和剩余数量的购买订单进行比较,并重复以上步骤
      • 最后如果购买订单有剩余,将剩余的购买订单价格和数量插入到 购买积压订单buy
  • 如果当前订单是销售订单,则看 购买积压订单 中是否有订单
    • 如果没有,将当前这批销售订单的 {price: '订单价格', amount: '订单数量'} 插入 销售积压订单sell 中,total 加上这批销售订单的数量
    • 如果有,弹出 购买积压订单buy 的堆顶元素
      • 如果堆顶订单的数量 大于 销售订单的数量,将堆顶订单的数量减去销售订单的数量,然后重新插入到 购买积压订单buy 中,total 减去这批销售订单的数量
      • 如果堆顶订单的数量 小于 销售订单的数量,将销售订单的数量减去堆顶订单的数量,total 减去堆顶订单的数量,继续弹出 购买积压订单buy 的堆顶元素和剩余数量的销售订单进行比较,并重复以上步骤
      • 最后如果销售订单有剩余,将剩余的销售订单价格和数量插入到 销售积压订单sell
  1. 返回结果 积压订单总数 total % (Math.pow(10, 9) + 7)

解题代码

var getNumberOfBacklogOrders = function(orders) {
    // 购买积压订单
    const buy = new Heap(1)
    // 销售积压订单
    const sell = new Heap(-1)
    // 积压订单总数
    let total = 0
    for (let [price, amount, orderType] of orders) {
        if (orderType === 0) { // 购买
            // 如果销售积压订单不为空 && 堆顶的价格小于等于购买订单的价格 && 购买订单数量大于0
            while (sell.size() && sell.top().price <= price && amount > 0) {
                const head = sell.pop()
                if (amount < head.amount) { // 如果购买订单数量小于堆顶订单数量
                    // 将多出的堆顶订单数量重新插入销售积压订单中
                    sell.push({price: head.price, amount: head.amount - amount})
                    // 积压订单总数少了被购买订单抵消的部分
                    total -= amount
                    // 购买订单数量置为0
                    amount = 0
                } else { // 购买订单数量大于等于堆顶订单数量
                    // 购买订单数量少了被堆顶订单抵消的部分
                    amount -= head.amount
                    // 积压订单总数少了被购买订单抵消的部分
                    total -= head.amount
                }
            }
            // 如果购买订单有剩余,将剩余的购买订单插入到购买积压订单
            if (amount > 0) {
                buy.push({price, amount})
                total += amount
            }
        } else { // 销售
            // 如果购买积压订单不为空 && 堆顶的价格大于等于销售订单的价格 && 销售订单数量大于0
            while (buy.size() && buy.top().price >= price && amount > 0) {
                const head = buy.pop()
                if (amount < head.amount) { // 如果销售订单数量小于堆顶订单数量
                    // 将多出的堆顶订单数量重新插入购买积压订单中
                    buy.push({price: head.price, amount: head.amount - amount})
                    // 积压订单总数少了被销售订单抵消的部分
                    total -= amount
                    // 销售订单数量置为0
                    amount = 0
                } else { // 销售订单数量大于等于堆顶订单数量
                    // 销售订单数量少了被堆顶订单抵消的部分
                    amount -= head.amount
                    // 积压订单总数少了被销售订单抵消的部分
                    total -= head.amount
                }
            }
            // 如果销售订单有剩余,将剩余的销售订单插入到销售积压订单
            if (amount > 0) {
                sell.push({price, amount})
                total += amount
            }
        }
    }
    return total % (Math.pow(10, 9) + 7)
};

// 堆类
class Heap {
    constructor(type) {
        this.arr = []
        // 根据 type 的值判断生成大顶堆还是生成小顶堆:-1 小顶堆  1 大顶堆
        this.type = type
    }

    // 返回堆的大小
    size() {
        return this.arr.length
    }

    // 返回堆顶元素
    top() {
        return this.arr[0]
    }

    // 往堆中插入元素
    push(val) {
        this.arr.push(val)
        this._sortBack()
    }

    // 弹出堆顶元素
    pop() {
        const val = this.arr[0]
        const back = this.arr.pop()
        if (this.size()) {
            this.arr[0] = back
            this._sortFront()
        }
        return val
    }

    // 向上调整堆结构
    _sortBack() {
        let i = this.size() - 1
        if (this.type === -1) {
            while (i > 0 && this.arr[i].price < this.arr[Math.floor((i - 1) / 2)].price) {
                [this.arr[i], this.arr[Math.floor((i - 1) / 2)]] = [this.arr[Math.floor((i - 1) / 2)], this.arr[i]]
                i = Math.floor((i - 1) / 2)
            }
        } else {
            while (i > 0 && this.arr[i].price > this.arr[Math.floor((i - 1) / 2)].price) {
                [this.arr[i], this.arr[Math.floor((i - 1) / 2)]] = [this.arr[Math.floor((i - 1) / 2)], this.arr[i]]
                i = Math.floor((i - 1) / 2)
            }
        }
    }

    // 向下调整堆结构
    _sortFront() {
        let i = 0
        if (this.type === -1) {
            while (i * 2 + 1 < this.size()) {
                let temp = i
                if (this.arr[temp].price > this.arr[i * 2 + 1].price) temp = i * 2 + 1
                if (i * 2 + 2 < this.size() && this.arr[temp].price > this.arr[i * 2 + 2].price) temp = i * 2 + 2
                if (temp === i) break
                [this.arr[temp], this.arr[i]] = [this.arr[i], this.arr[temp]]
                i = temp
            }
        } else {
            while (i * 2 + 1 < this.size()) {
                let temp = i
                if (this.arr[temp].price < this.arr[i * 2 + 1].price) temp = i * 2 + 1
                if (i * 2 + 2 < this.size() && this.arr[temp].price < this.arr[i * 2 + 2].price) temp = i * 2 + 2
                if (temp === i) break
                [this.arr[temp], this.arr[i]] = [this.arr[i], this.arr[temp]]
                i = temp
            }
        }
    }
}