剑指–队列的最大值
1,题目:
2,思路:
看下面的代码
3,代码:
写法一:(队列+两个栈):
class MaxQueue {
/*
等下,我们使用一个队列正常的存放数据,
然后使用维护一个单调栈,栈顶为最大值
再加一个辅助栈
元素插入顺序:1 3 5 5 2 6 1 2
(栈的元素最左边为栈底,最右边为栈顶,队列也一样)
插入 1:
queue = {1}
stack = {1}
插入 3
queue = {3, 1}
stack = {3}
(
这里为什么 stack 是 3?之前的 1 哪去了?
因为 1 比 3 先插入,因此最先 poll() 的必定会是 1,而在 poll 3 之前,无论 1 是否存在,最大值都会是 3,因此 1 的存在无关紧要
这里我们就可以知道,只要后面进来一个比之前存储元素都要大的,那么前面的小元素都无关紧要了,清空栈,只入栈这个最大元素
)
插入 5
queue = {5, 3, 1}
stack = {5}
插入 5
queue = {5,5, 3, 1}
stack = {5,5}
(
插入跟栈顶元素一样大的值,这个就不必多说什么了吧,直接入栈即可
)
插入 2
queue = {2, 5, 5, 3, 1}
stack = {2, 5, 5}
(
这里为什么 2 又存在了? 而且是在栈底?
因为 5 比 2 先插入,因此最先 poll() 的一定会是 5,那么当 poll 5 的时候,前面的 1 和 3 也都已经 poll() 了
那么队列剩下的就是 5 后面插入元素的最大值,即 2,因此我们需要留下 2
)
插入 6
queue = {6, 2, 5, 5, 3, 1}
stack = {6}
插入 1
queue = {1, 6, 2, 5, 5, 3, 1}
stack = {1, 6}
插入 2
queue = {2, 1, 6, 2, 5, 5, 3, 1}
stack = {2, 6}
(
这里为什么是 2 在底部? 1 哪去了?
6 比 后面的 1 和 2 都先插入,因此在 poll 6 之前, 6 是最大的
而 1 比 2 先插入,因此在 poll 2 之前,前面的 1 是否存在无关紧要,当 poll 6 后,最大值就会是 6 后面插入元素的最大值。即 2
这里我们就知道,我们当插入元素比 栈顶元素 小的时候,我们需要清空掉 栈内所有比 插入元素小的值,然后入栈
(这里做法就是 面试题 03.05. 栈排序 的做法)
)
*/
Queue<Integer> queue;//队列
Deque<Integer> stack;//栈
Deque<Integer> helper_stack;//辅助栈
public MaxQueue() {
queue = new LinkedList<>();
stack = new LinkedList<>();
helper_stack = new LinkedList<>();
}
public int max_value() {
if(queue.isEmpty()){
return -1;
}
return stack.peek();//返回最大值
}
public void push_back(int value) {
queue.add(value);
//栈为空,直接入栈
if(stack.isEmpty()){
stack.push(value);
}else{
if(stack.peek() < value){ //当栈顶元素比当前元素小,那么清空栈,然后将 value 入栈
stack.clear();
stack.push(value);
}else if(stack.peek() == value){ //当栈顶元素等于当前元素,那么直接入栈
stack.push(value);
}else{
while(!stack.isEmpty() && stack.peek() >= value){ //当栈顶元素大于当前元素,将大于等于 value 的值压入辅助栈
helper_stack.push(stack.pop());
}
//压入插入元素
helper_stack.push(value);
//清空 stack
stack.clear();
//重新将辅助栈的值压回 stack
while(!helper_stack.isEmpty()){
stack.push(helper_stack.pop());
}
}
}
}
public int pop_front() {
if(queue.isEmpty()){
return -1;
}
int temp = queue.poll();
if(stack.peek() == temp){
stack.pop();
}
return temp;
}
}
写法二:(队列+双端队列):
class MaxQueue {
Queue<Integer> que;
Deque<Integer> deq;
public MaxQueue() {
que = new LinkedList<>(); //队列:插入和删除
deq = new LinkedList<>(); //双端队列:获取最大值
}
public int max_value() {
return deq.size()>0?deq.peek():-1; //双端队列的队首为que的最大值
}
public void push_back(int value) {
que.offer(value); //value入队
while(deq.size()>0 && deq.peekLast()<value){
deq.pollLast(); //将deq队尾小于value的元素删掉
}
deq.offerLast(value); //将value放在deq队尾
}
public int pop_front() {
int tmp = que.size()>0?que.poll():-1; //获得队首元素
if(deq.size()>0 && deq.peek().equals(tmp)){
deq.poll(); //如果出队的元素是当前最大值,将deq的队首出队
}
return tmp;
}
}
写法三:队列+双端链表:
public class MaxQueue {
Queue<Integer> queue;
LinkedList<Integer> max;
public MaxQueue() {
queue = new LinkedList<>();
max = new LinkedList<>();//LinkedList是双端链表
}
public int max_value() {
return max.size()==0?-1:max.getFirst();
}
public void push_back(int value) {
queue.add(value);
while(max.size()!=0&&max.getLast()<value){//注意:这里第二个判断条件不能带等号,即max中对于当前queue中的具有相同值的元素会全部存储,而不是存储最近的那个。
max.removeLast();
}
max.add(value);
}
public int pop_front() {
if(max.size()!=0&&queue.peek().equals(max.getFirst()))//Integer类型的值的比较不能直接使用==
max.removeFirst();
return queue.size()==0?-1:queue.poll();
}
}