题目描述
请定义一个队列并实现函数 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
思路一(暴力)
使用数组模拟一个队列的插入与删除,最大值通过遍历查找。
复杂度分析:
时间复杂度:O(1)(插入,删除),O(n)(求最大值)。
插入与删除只需要普通的队列操作,为 O(1),求最大值需要遍历当前的整个队列,最坏情况下为 O(n)。
空间复杂度:O(n),需要用队列存储所有插入的元素。
class MaxQueue {
public:
int q[20000],begin=0,end=0;
MaxQueue() {
}
int max_value() {
int ans = -1;
for(int i=begin;i<end;i++)
ans = max(q[i],ans);
return ans;
}
void push_back(int value) {
q[end++]=value;
}
int pop_front() {
if(begin==end)
return -1;
return q[begin++];
}
};
思路二(单调递减双端队列)
使用普通队列处理pop返回,单调递减双端队列处理max返回。根据入队顺序,若为{1,3,2},再依次出队每次输出最大值,那么当3入队时,双端队列从尾端弹出1,因为为了存储最大值序列的双端队列中的1已经无效,无论如何3会比1后出队(先入先出原则),那么此时双端队列中仅有3;当2入队时,双端队列直接在后端插入2,因为2与尾端3比较较小,这样也能保证在1,3出队后,双端队列依然能够输出此时的最大值2。
复杂度分析:
时间复杂度:O(1)(插入,删除,求最大值)
删除操作于求最大值操作显然只需要 O(1)的时间。
而插入操作虽然看起来有循环,做一个插入操作时最多可能会有n次出队操作。但要注意,由于每个数字只会出队一次,因此对于所有的n个数字的插入过程,对应的所有出队操作也不会大于n次。因此将出队的时间均摊到每个插入操作上,时间复杂度为 O(1)。
空间复杂度:O(n),需要用队列存储所有插入的元素。
class MaxQueue {
public:
queue<int> q;
deque<int> d;
MaxQueue() {
}
int max_value() {
if(d.empty())
return -1;
return d.front();
}
void push_back(int value) {
//依次出队尾部较小元素
while(!d.empty() && d.back() < value)
d.pop_back();
d.push_back(value);
q.push(value);
}
int pop_front() {
if(q.empty())
return -1;
int ans = q.front();
//出队的是最大值
if(ans == d.front())
d.pop_front();//递减双端队列头部弹出
q.pop();
return ans;
}
};