From f46902b970ae1826c13963b9cc713379daa8c4eb Mon Sep 17 00:00:00 2001 From: Sainnhe Park Date: Thu, 15 Dec 2022 17:11:43 +0800 Subject: [PATCH] Update queue --- notes/src/stack_and_queue.md | 60 +++++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/notes/src/stack_and_queue.md b/notes/src/stack_and_queue.md index 58ef3ff..3d18945 100644 --- a/notes/src/stack_and_queue.md +++ b/notes/src/stack_and_queue.md @@ -18,7 +18,7 @@ 使用场景: 1. 先进先出的数据结构 -2. 滑动窗口 +2. 滑动窗口最值问题 ### 变体:优先级队列 @@ -65,6 +65,64 @@ Not empty 经典题目:[239. 滑动窗口最大值](https://leetcode.cn/problems/sliding-window-maximum/) +奇技淫巧:随机删除元素 + +现在我们想以 `O(logn)` 的时间复杂度来删除元素,应该怎么做呢? + +思路很简单,维护另一个优先队列——删除队列。 + +当我们要删除一个元素时,我们并不真的在原队列中删除它,而是把它放到删除队列中; + +当我们要访问原队列栈顶时,看看原队列栈顶是不是等于删除队列栈顶,如果是,则循环删除原队列和删除队列的栈顶,直到栈顶不想等或者为空。 + +为什么这样做一定是正确的呢?有没有可能我们想要删除 x ,原队列栈顶也是 x ,而删除队列栈顶是 y ( y >= x ) 呢? + +这是不可能的,因为如果 y >= x ,那么原队列栈顶就不可能是 x 。 + ### 变体:单调队列 +优先队列有另外一个名字:二叉堆,它的数据结构本质上并不是队列,时间复杂度不是线性的。 + +是否有线性复杂度的数据结构呢? + +有,这就是单调队列。 + +单调队列的特性如下: + +1. 它的数据结构本质依然是队列,因此具有线性时间复杂度 +2. 它并不能保证你弹出一个元素后,这个元素就真的在队列里删除了,也有可能当你在插入元素时会删掉队列里的其它元素 +3. 它能保证的是,队列中所有元素单调(递减),队首元素一定是队列里的最值。 + 经典题目:[239. 滑动窗口最大值](https://leetcode.cn/problems/sliding-window-maximum/) + +我们看看这道题里的单调队列是怎么实现的。这里我们采用 `deque` ,相比于 `queue` ,它能同时对队首和队尾进行操作。 + +```cpp +#include + +class MyQueue { + public: + std::deque que; + + void pop(int value) { + // 只有当要弹出的元素等于队首时,才会弹出 + // 这样做没问题吗? + // 没问题,因为我们只关注队首元素是不是最大的,只要我们想弹出的元素不是队首元素,那就可以不用管 + if (value == que.front() && !que.empty()) { + que.pop_front(); + } + } + + void push(int value) { + // 当我们要插入的元素比队尾元素大时,就一直弹出队尾元素,直到小于等于队尾元素为止? + // 这样做没问题吗? + // 没问题,因为我们只关注队首元素是不是最大的,其它元素要不要都无所谓。 + while (value > que.back() && !que.empty()) { + que.pop_back(); + } + que.push_back(value); + } + + int front(void) { return que.front(); } +}; +```