栈 队列 优先队列(堆) 单调栈 单调队列
文章目录
理论基础
队列和栈都是容器适配器,也就是可以用 vector, list, deque
作为底层实现的容器,一般默认是用 deque
来实现。
因此栈和队列在内存中的分布是否连续是根据使用的容器而定,如果用 deque, list
实现则是不连续分布,而 vector
则是连续分布。
20.括号匹配
通过 mp
把括号对应成整数从而快速判断。
bool isValid(string s) {
unordered_map<char, int> mp;
mp['('] = 1, mp[')'] = -1, mp['['] = 2, mp[']'] = -2, mp['{'] = 3, mp['}'] = -3;
stack<int> stk;
for (auto c : s)
{
int t = mp[c];
if (t > 0)
{
stk.push(t);
continue;
}
if (stk.empty()) return false;
int p = stk.top();
stk.pop();
if (t + p != 0) return false;
}
return stk.empty();
}
232. 用栈模拟队列
开两个栈,一个作为输出栈,一个作为输入栈,建议 pop(), top()
函数复用。
225. 用队列模拟栈
- 一个队列:队头出队后再入队;
- 两个队列:一个队列作为另一个队列的拷贝使用。
347. TOP K Frequnency
- 大顶堆
O(nlogn)
小顶堆(大小为k)O(nlogk)
priority_queue<PII, vector<PII>, greater<PII>> q;
维护
sz
判断堆元素大小,sz < k
则加入元素,否则和堆顶判断。
- 计数排序
O(n)
维护一个数组 s[i]
统计出现次数为 i
的元素个数有几个,之后维护下标 t
从 n
往 0
统计(一个元素最多出现 n
次),直到出现次数累加 sum ≥ k
,最后再遍历一遍数组,如果出现次数大于 t
那该元素就是前 k
多的元素。
vector<int> topKFrequent(vector<int>& nums, int k) {
int n = nums.size();
unordered_map<int, int> mp;
for (auto x : nums) mp[x] ++;
vector<int> s(n + 1);
for (auto p : mp) s[p.second] ++;
int t = n, sum = 0;
while (sum < k) sum += s[t --];
vector<int> res;
for (auto p : mp)
if (p.second > t) res.push_back(p.first);
return res;
}
42. 接雨水 [字节常考题]
-
桶方式 (三次线性扫描)
把每列视作宽度为
1
的桶,而桶能装多少水取决于桶最低侧的高度。左侧的桶高就是左侧柱子的最大值,右侧同理,因此遍历2次记录每列左侧右侧的最大值,该列的桶能接住的水就是max(0, min(lmax, rmax) - h)
。 -
单调栈
维护一个单调递减的栈,当遇到新的柱子高度比当前栈顶元素
h[stk.top()]
大,则说明遇到了一个凹槽,此时弹出栈顶idxx
,该凹槽的两端分别为新的栈顶元素(如果存在的话)和当前遍历到的柱子。凹槽的宽度为
i - stk.top() - 1
,高度为min(h[stk.top()], h[i]) - h[idxx]
。
AcWing 154. 滑动窗口
单调队列往往用来求滑动窗口的最大/小值。
以求滑动窗口最大值为例,基本思想就是首先判断队首元素还在不在窗口之中,之后在我左边且比我小的永远不可能是答案,因为只要我在一天它就没有出头之日。
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int> res;
deque<int> q;
int n = nums.size();
for (int i = 0; i < n; i ++ )
{
if (!q.empty() && q.front() < i - k + 1) q.pop_front();
while (!q.empty() && nums[q.back()] <= nums[i]) q.pop_back();
q.push_back(i);
if (i >= k - 1) res.push_back(nums[q.front()]);
}
return res;
}
AcWing 830. 单调栈
单调栈往往用来求每个元素左/右第一个比它大/小的元素。
以找每个元素左边第一个比它小的元素为例,在我左边且比我大的永远不可能是答案。
for(int i = 0; i < n; i ++)
{
cin >> x;
while (!s.empty() && s.top() >= x) s.pop();
if (!s.empty()) cout << s.top() << ' ';
else cout << -1 << ' ';
s.push(x);
}