题目
请定义一个队列并实现函数 max_value 得到队列里的最大值,要求函数max_value、push_back 和 pop_front 的均摊时间复杂度都是O(1)。
若队列为空,pop_front 和 max_value 需要返回 -1
示例 1:
输入:
["MaxQueue","push_back","push_back","max_value","pop_front","max_value"]
[[],[1],[2],[],[],[]]
输出: [null,null,null,2,1,2]
示例 2:
输入:
["MaxQueue","pop_front","max_value"]
[[],[],[]]
输出: [null,-1,-1]
限制:
- 1 <= push_back,pop_front,max_value的总操作数 <= 10000
- 1 <= value <= 10^5
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/dui-lie-de-zui-da-zhi-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
这题要实现一个队列的查询最大值的功能,同时要求查询、插入和删除的时间复杂度都是O(1)。对于一个队列而言,插入和删除的时间复杂度本来就是O(1),关键在于查询最大值的时间复杂度。如果我们用一个数组来存储队列的值,那么每次插入和删除后再查询最大值的时间复杂度就是O(n)。该用一个什么样的数据结构来实现查询最大值就是这个问题的关键点了,它需要这样几点:
- 插入和删除的均摊时间复杂度是O(1)级别;
- 查询最大值的时间复杂度是O(1)。
我们注意到队列插入元素是在尾部,而删除元素是从头部开始。也就是说,为了实现查询最大值,我们在插入一个元素时,需要将其和之前队列中的最大值比较并更新最大值;而在删除一个元素时,则需要确定删除的值是不是目前队列中的最大值,如果是,那么就需要更新最大值,同时更新最大值这个动作的均摊时间复杂度得是O(1)。我们可以用单调队列来实现,构建一个双端队列,保持其从前到后为递减的顺序,这样查询最大值就只需要取这个双端队列头部的第一个元素;插入元素时,则是将当前要插入的元素和队列末尾的元素比较,删除双端队列尾部比待插入元素小的数,直至队列为空或者队列末尾元素大于当前元素;删除队列时,比较原队列开头元素是否也是双端队列的头元素,如果是就也删除双端队列的头元素,如果不是那就说明删除原队列头元素不影响其最大值,不必处理。
空间复杂度O(n),max_value的时间复杂度显然是O(1),对于n个元素的插入和删除,pop_front()和push_back()的总的时间复杂度是O(n)级别,对于每一个元素来说均摊时间复杂度是O(1)。
代码
class MaxQueue {
public:
MaxQueue() {
}
int max_value() {
return d.empty() ? -1 : d.front();
}
void push_back(int value) {
q.push(value);
while(!d.empty() && value > d.back()){
d.pop_back();
}
d.push_back(value);
}
int pop_front() {
if(q.empty()) return -1;
int n = q.front();
q.pop();
if(!d.empty() && n == d.front()){
d.pop_front();
}
return n;
}
private:
queue<int> q;
deque<int> d;
};
/**
* Your MaxQueue object will be instantiated and called as such:
* MaxQueue* obj = new MaxQueue();
* int param_1 = obj->max_value();
* obj->push_back(value);
* int param_3 = obj->pop_front();
*/