单调栈
单调栈是一种运用栈数据结构来解决一些特定问题的解题技巧。它主要用于解决需要快速寻找一个元素左(或右)边第一个比它大(或小)的元素的问题。
使用单调栈的基本思路是保持栈内的元素单调递增或单调递减,栈顶元素是当前栈内最大或最小的元素,同时记录下每个元素的相关信息,如坐标、面积、数量等,根据这些信息计算出所需的结果。
单调栈的实现主要有两种,第一种是从前往后遍历数组,第二种是从后往前遍历数组。
寻找数组右侧第一个比它大的数
第一种实现方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| vector<int> nextGreaterElement(vector<int> &nums) { int n = nums.size(); stack<int> s; vector<int> res(n, -1);
for(int i = 0; i < n; ++i) { while(!s.empty() && nums[i] > nums[s.top()]) { res[s.top()] = i; s.pop(); } s.emplace(i); } return res; }
|
第二种实现方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| vector<int> nextGreaterElement(vector<int> &nums) { int n = nums.size(); stack<int> s; vector<int> res(n, -1);
for(int i = n - 1; i >= 0; --i) { while(!s.empty() && nums[s.top()] <= nums[i]) s.pop(); res[i] = s.empty() ? -1 : s.top(); s.emplace(i); } }
|
寻找数组右侧第一个比它小的数
第一种实现方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| vector<int> nextSmallerElement(vector<int> &nums) { int n = nums.size(); stack<int> s; vector<int> right(n, -1);
for(int i = 0; i < n; ++i) { while(!s.empty() && nums[i] <= nums[s.top()]) { right[s.top()] = i; s.pop(); } s.emplace(i); } }
|
第二种实现方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| vector<int> nextSmallerElement(vector<int> &nums) { int n = nums.size(); stack<int> s; vector<int> right(n, -1); for(int i = n - 1; i >= 0; --i) { while(!s.empty() && nums[i] <= nums[s.top()]) s.pop(); right[i] = s.empty() ? -1 : s.top(); s.emplace(i); } return right; }
|
寻找数组右侧第一个满足某个条件的数
观察上面两个算法的实现,可以发现,以从前往后遍历为例,他们的框架其实是一样的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| vector<int> nextSatisfiedElement(vector<int> &nums) { int n = nums.size(); stack<int> s; vector<int> res(n, -1);
for(int i = 0; i < n; ++i) { while(!s.empty() && `nums[i]满足nums[s.top()]的条件`) { res[s.top()] = i; s.pop(); } s.emplace(i); } return res; }
|
从后往前遍历:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| vector<int> nextSatisfiedElement(vector<int> &nums) { int n = nums.size(); stack<int> s; vector<int> right(n, -1); for(int i = n - 1; i >= 0; --i) { while(!s.empty() && `nums[s.top()]不满足nums[i]的条件`) s.pop(); right[i] = s.empty() ? -1 : s.top(); s.emplace(i); } return right; }
|
寻找数组左侧第一个满足某个条件的数
从前往后遍历:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| vector<int> nextSatisfiedElement(vector<int> &nums) { int n = nums.size(); stack<int> s; vector<int> left(n, -1); for(int i = n - 1; i >= 0; --i) { while(!s.empty() && `nums[s.top()]不满足nums[i]的条件`) s.pop(); left[i] = s.empty() ? -1 : s.top(); s.emplace(i); } return right; }
|
从后往前遍历:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| vector<int> nextSatisfiedElement(vector<int> &nums) { int n = nums.size(); stack<int> s; vector<int> left(n, -1); for(int i = n - 1; i >= 0; --i) { while(!s.empty() && `nums[i]满足nums[s.top()]的条件`) { left[s.top()] = i; s.pop(); } s.emplace(i); } return right; }
|
attention
寻找左边第一个满足某个条件的数和寻找右边第一个满足条件的数,从前往后遍历和从后往前遍历的循环条件刚好是相反的