题目

LCR 184. 设计自助结算系统 - 力扣(LeetCode)

请设计一个自助结账系统,该系统需要通过一个队列来模拟顾客通过购物车的结算过程,需要实现的功能有:

  • get_max():获取结算商品中的最高价格,如果队列为空,则返回 -1
  • add(value):将价格为 value 的商品加入待结算商品队列的尾部
  • remove():移除第一个待结算的商品价格,如果队列为空,则返回 -1

注意,为保证该系统运转高效性,以上函数的均摊时间复杂度均为 O(1)

示例 1:

输入:

["Checkout","add","add","get_max","remove","get_max"]
[[],[4],[7],[],[],[]]

输出:

[null,null,null,7,4,7]

示例 2:

输入:

["Checkout","remove","get_max"]
[[],[],[]]

输出:

[null,-1,-1]

提示:

  • 1 <= get_max, add, remove 的总操作数 <= 10000
  • 1 <= value <= 10^5

分析

为了实现较低的时间复杂度,不能每次获取最大值的时候,都去计算最大值,因此只能在构建数据的过程中就实时计算每次的最大值,并保存下来,这样的话需要获取最大值的时候只需要读取就能达到很快的速度。
而为了每次获取的时候都实时计算最大值,因此需要分析队列的数据构建方式。

首先队列是先进先出,后进后出的数据结构。

为了方便分析,分成两种情况去进行分析,分别是插入和释放的两种情况。

插入分析

现在先考虑插入场景,而暂时不考虑移除场景。

首先插入队列数据queue时候,维护一个最大值队列max。

queue  1
max    1

随着数据的增加,明显,数据队列的最大值要跟随数据队列变化

当新增数据比之前数据大的时候,要做最大值合并。 最大值要始终覆盖成最大的,然后更新到最大值队列max中保存下来。

比如 先插入 1 2 3, 则最大值队列max中最大值是 3, 继续插入 4 5 ,则 最大值队列中的3 被依次替换成 4 , 5, 最后是 5:

queue  1 2 3 4 5
max    5

当新增数据比前面数据小的时候,最大值队列的数据不作变更, 而是以这次开始变小的数据为界限,再次统计最大值。至于为什么要再次统计,是因为当前面最大值数据被释放的时候(队列是先进先出),这个新增的最大值数据就是数据队列剩余值的最大值。

比如 继续依次插入 1 2 3 , 则最大值队列max的值会依次变成 5 1, 5 2, 5 3, 最后是 5 3:

queue  1 2 3 4 5 1 2 3
max    5 3

当插入的数据和最大值相等的时候,为了方便删除也需要将将这个数据插入最大值队列中。

queue  1 2 3 4 5 1 2 3 3
max    5 3 3

当插入的数据总是比前面数据小的时候,不需要做最大值合并, 当插入数据比前面数据大的时候, 最大队列要始终进行合并直到和自身为最大为止。
比如数据队列继续插入 6 的时候, 6 总是比前面大,因此前面数据都被依次合并,最后最大值队列max的值变为6:

queue  1 2 3 4 5 1 2 3 3 6
max    6

释放分析

现在再单独考虑释放的场景

使用 插入 6 之前的数据,数据如下:

queue  1 2 3 4 5 1 2 3 3
max    5 3 3

释放数据队列queue中的数据的时候,只需要比较释放的数据是否为最大值队列max中的值即可,是的话最大值队列max面的值也一并释放, 当数据队列释放 1 … 5 的值时候,数据队列的值会变成 queue 1 2 3 3, 最大值队列前面的 5 被释放掉,剩余 3 3。 效果如下:

queue 1 2 3 3
max   3 3

因为插入数据的时候,判断了插入值和最大值队列max尾部数据相同的时候也插入最大值队列中,因此当继续释放 1 2 3 的时候, 最大值队列也释放了一个 3 后,还会剩余一个3,结果还是能获取到数据队列queue的最大值,如下:

queue  3
max    3

分析结论汇总

因此将规则汇总如下:

  • 插入数据(尾插)时
    • 如果插入queue的数据,从最大值队列max的后面往前面比较,总是比max前面的数据大,则从后往前递归合并最大值队列max的值,也就是删除小于插入数据的值。
    • 如果插入queue的数据和最大值队列max尾部的数据相等,则也将这个数据插入最大值队列max中。
    • 如果插入queue的数据小于最大值队列max尾部的数据,则也将这股数据插入最大值队列max中。
  • 释放数据(头释)时,当释放的数据等于最大值队列max中的值,则最大值队列max也一起释放。

考虑到需要针对最大值队列max递归从后往前进行数据比较和释放,因此最大值队列max需要使用双向队列来进行存储。

代码

#include <queue>
#include <deque>
 
class Checkout {
public:
    Checkout()
    {
    }
 
    int get_max()
    {
        return m_q.empty() ? -1 : m_max.front();
    }
 
    void add(int value)
    {
        if (m_max.size() && value > m_max.back()) {
            auto prev = m_max.end();
 
            for (auto it = m_max.end() - 1; it != m_max.begin() - 1; --it) {
                if (value > *it) {
                    prev = it;
                }
            }
            if (prev != m_max.end())
                m_max.erase(prev, m_max.end());
        }
        m_max.push_back(value);
        
        m_q.push(value);
    }
 
    int remove()
    {
        if (m_q.empty())
            return -1;
 
        int val = m_q.front();
        m_q.pop();
 
        if (val == m_max.front()) {
            m_max.pop_front();
        }
 
        return val;
    }
    
private:
    std::queue<int> m_q;
    std::deque<int> m_max;
};
 
/**
 * Your Checkout object will be instantiated and called as such:
 * Checkout* obj = new Checkout();
 * int param_1 = obj->get_max();
 * obj->add(value);
 * int param_3 = obj->remove();
 */
 
#ifdef __TEST__
 
#include <iostream>
 
int main(int argc, char* argv[])
{
    Checkout *c = new Checkout();
 
    c->add(1);
    c->add(2);
    c->add(3);
    std::cout << "max: 3 == " << c->get_max() << "\n";
    c->add(4);
    c->add(5);
    std::cout << "max: 5 == " << c->get_max() << "\n";
    c->add(1);
    c->add(2);
    c->add(3);
    c->add(3);
    std::cout << "max: 5 == " << c->get_max() << "\n";
    
    c->remove();
    c->remove();
    c->remove();
    c->remove();
    std::cout << "max: 5 == " << c->get_max() << "\n";
    c->remove();
    std::cout << "max: 3 == " << c->get_max() << "\n";
    c->remove();
    std::cout << "max: 3 == " << c->get_max() << "\n";
 
    c->add(6);
    std::cout << "max: 6 == " << c->get_max() << "\n";
 
 
    delete c;
 
    return 0;
}
 
#endif //__TEST__
 

*****