Files
code-docs/docs/LeetCode/CodingInterviews/StackAndQueue/MaxValueOfQueue.md
T
Chuyaoyuan 64582c4a57 支持数学公式和docs页面修改
支持数学公式和docs页面修改
2021-05-13 00:16:22 +08:00

184 lines
6.5 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 队列的最大值问题
## 题目描述如下:
请定义一个队列并实现函数max\_value得到队列里的最大值,要求函数max\_value、push\_back和pop\_front的均摊时间复杂度都是O(1)。若队列为空,pop\_front和max\_value需要返回-1。
示例1:
~~~shell
输入:
["MaxQueue","push_back","push_back","max_value","pop_front","max_value"]
[[],[1],[2],[],[],[]]
输出: [null,null,null,2,1,2]
~~~
示例2:
```shell
输入:
["MaxQueue","pop_front","max_value"]
[[],[],[]]
输出: [null,-1,-1]
```
限制:
- `1 <= push_back,pop_front,max_value的总操作数 <= 10000`
- `1 <= value <= 10^5`
代码:
~~~java
class MaxQueue {
public MaxQueue() {
}
public int max_value() {
}
public void push_back(int value) {
}
public int pop_front() {
}
}
/**
* 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();
*/
~~~
## 思考思路
题目的描述中提到了3个方法,其中max\_value是求最大值的,**提到最大值,首先想到这里应该是用到了某种排序的手段**。剩下的两个方法分别是push和pop操作,这明显是栈、队列这块的操作。因此不难想象,这道题用到的数据结构包含某种队列,然后涉及到某种排序方法。
本着先实现,后优化的原则,最容易想到的解决方案是这样的:
<span style={{color:'blue'}}>用一个队列保存所有的数据,这样保证了push和pop操作是正常执行的。然后将这个队列从大到小排列到另一个数据结构中,这样执行max_value方法时输出数组第一项即可。删除队列中数据的时候,用来排序的数据结构中的对应数据也应该删掉。</span>
于是乎很容易写出这样的代码:
~~~java
class MaxQueue {
private Queue datas;
// TODO:排序后的数据暂时用队列保存
private Queue sortedDatas;
public MaxQueue() {
this.datas = new LinkedList<Integer>;
this.sortedDatas = new LinkedList<Integer>;
}
public int max_value() {
if (sortedDatas.isEmpty()) {
return -1;
}
return sortedDatas.peekFirst();
}
public void push_back(int value) {
datas.offer(value);
sortedDatas = addNewDataAndSortDatas();
}
public int pop_front() {
if (datas.isEmpty()) {
return -1;
}
int ans = datas.pool();
deleteDataFromSortedDatas(ans);
return ans;
}
private Queue addNewDataAndSortDatas() {
// TODO: 将所有数据排序,然后覆盖原先的sortedDatas
}
private void deleteDataFromSortedDatas(int ans) {
// TODO: 将ans从sortedDatas队列中删除
}
}
~~~
接下来考虑着三个TODO项如何去解决。首先要确定的是<span style={{color:'red'}}>用什么数据结构保存sortedDatas</span>。
| 数据结构 | 优劣分析 | 结论 |
| -------- | ------------------------------------------------------------ | ---------- |
| 数组 | 队列中元素个数能达到10000,定义10000长度的数组浪费内存,不定义这么长要考虑扩容,浪费性能。 | :no_entry: |
| 链表 | 不可能用这货排序。 | :no_entry: |
| 栈 | 将数据由小到大存入,能直接取到最大值。删除数据时要再用一个栈倒数据。**正常的排序方法难以实现均摊时间复杂度O(1)**。所以能实现但是时间复杂度不符合要求。 | |
| 队列 | 单调递减的队列。遇到的问题和栈一致。 | |
| 树 | 最大二叉树能实现,但是树在内存中也是以数组的形式保存的,问题和数组一致。 | :no_entry: |
所以说,要么用栈,要么用数组来保存sortedDatas中的数据。所以决定采用双端队列来处理sortedDatas,因为**双端队列能同时满足栈、队列的所有操作**。下一个问题便是……<span style={{color:'red'}}>如何给这个双端队列进行排序</span>。
堆排序、快速排序的时间复杂度是O(n lg n);其他的线性排序算法时间复杂度是O(n)。这些都不太满足题目的要求。所以这里我想到了一种可能性:**有些数是不是没有参与排序**?如果队列中有n个数字,参与排序的数字小于n,那么排序的时间复杂度会更小的。那么,**什么样的数字可能不用参与排序呢**?max\_value方法要输出的是最大的数,所以最大的数字是一定参与排序的,即比较小的数字可以不用参与排序——我只管最大的数,其他的数字顺序对与否甚至数据的有无,都不会影响到最后的结果。
所以进一步的解决方案是这样的:
<span style={{color:'red'}}>每次有新的数据插入的时候,将sortedDatas中的数据从后往前跟入参比较,如果小于入参,就直接舍弃掉,直到遇到比入参大的数字时再将入参存入队列中。</span>
这样的好处显而易见:小于入参的数字不会影响到最大值的输出。另一方面,队列中的数据来得比现在这个入参早,所以小于入参的数字会比这个入参更早的被删除掉。即,小于入参的数字永远不可能作为最大值背输出出来。
所以优化后的代码是这个样子的:
~~~java
class MaxQueue {
Queue<Integer> datas;
Deque<Integer> sortedDatas;
public MaxQueue() {
datas = new LinkedList<Integer>();
sortedDatas = new LinkedList<Integer>();
}
public int max_value() {
if (sortedDatas.isEmpty()) {
return -1;
}
return sortedDatas.peekFirst();
}
public void push_back(int value) {
while (!sortedDatas.isEmpty() && sortedDatas.peekLast() < value) {
sortedDatas.pollLast();
}
sortedDatas.offerLast(value);
datas.offer(value);
}
public int pop_front() {
if (datas.isEmpty()) {
return -1;
}
int ans = datas.poll();
if (ans == sortedDatas.peekFirst()) {
sortedDatas.pollFirst();
}
return ans;
}
}
/**
* 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();
*/
~~~
此题得解。