【LeetCode】22.单调栈系列
总目录:
0.理论基础
0.1.定义
对于一个序列,要求每个元素左/右边第1个比自己大/小的距离差时,就适用单调栈。
单调栈,以普通的栈为基础,只是入栈时要保持栈的单调性,如果新入栈元素会破坏单调性则将栈内原有元素弹栈直到符合单调性时再让新元素入栈。
非常详细,单调栈性质探究:http://t.zoukankan.com/molinchn-p-14772025.html
0.2.操作
单调栈在旧元素出栈、新元素入栈前产生操作。
1 // insert element x in mono_stack 2 void insert(stack<int>& mono_stack, int x) { 3 while (!mono_stack.empty() && mono_stack() > x) { 4 // operations #1 5 mono_stack.pop(); 6 } 7 // operations #2 8 mono_stack.push(x); 9 }
根据要求每个元素左/右边第1个比自己大/小的距离差的需求不同,单调栈的单调设计、出栈还是入栈时生成数据存在排列组合,按需使用,从左往右/从右往左遍历都可以。
0.3.代码实例
第4题经典的接雨水、第5题能勾勒的最大面积问题
1.每日温度
1.1.问题描述
给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指对于第 i 天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用 0 来代替。
链接:https://leetcode.cn/problems/daily-temperatures
1.2.要点
向右找第一个较大值
1.3.代码实例
1 class Solution { 2 public: 3 vector<int> dailyTemperatures(vector<int>& T) { 4 // 递增栈 5 stack<int> st; 6 vector<int> result(T.size(), 0); 7 st.push(0); 8 for (int i = 1; i < T.size(); i++) { 9 if (T[i] < T[st.top()]) { // 情况一,后者小于栈顶 10 st.push(i); 11 } else if (T[i] == T[st.top()]) { // 情况二,后者等于栈顶 12 st.push(i); 13 } else { 14 while (!st.empty() && T[i] > T[st.top()]) { // 情况三,后者大于栈顶,为所求 15 result[st.top()] = i - st.top(); 16 st.pop(); 17 } 18 st.push(i); 19 } 20 } 21 return result; 22 } 23 };
2.下一个更大元素——到尾为止
2.1.问题描述
nums1 中数字 x 的 下一个更大元素 是指 x 在 nums2 中对应位置 右侧 的 第一个 比 x 大的元素。
给你两个 没有重复元素 的数组 nums1 和 nums2 ,下标从 0 开始计数,其中nums1 是 nums2 的子集。
对于每个 0 <= i < nums1.length ,找出满足 nums1[i] == nums2[j] 的下标 j ,并且在 nums2 确定 nums2[j] 的 下一个更大元素 。如果不存在下一个更大元素,那么本次查询的答案是 -1 。
返回一个长度为 nums1.length 的数组 ans 作为答案,满足 ans[i] 是如上所述的 下一个更大元素 。
链接:https://leetcode.cn/problems/next-greater-element-i
2.2.要点
相比于每日温度问题,只是多了一个nums1和nums2映射的问题
2.3.代码实例
1 class Solution { 2 public: 3 vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) { 4 int len1=nums1.size(); 5 int len2=nums2.size(); 6 vector<int> ans(len1,-1); 7 8 //记录nums2中每个元素的位置,方便nums1中的元素进行索引 9 unordered_map<int,int> numMap; 10 for(int i=0;i<len2;i++){ 11 numMap[nums2[i]]=i; 12 } 13 14 //单调栈用于记录nums2中每个元素右边的第一个较大元素 15 vector<int> temp(len2,-1); 16 stack<int> monoSt; 17 for(int i=0;i<len2;i++){ 18 while(!monoSt.empty()&&nums2[monoSt.top()]<nums2[i]){ 19 temp[monoSt.top()]=nums2[i]; 20 monoSt.pop(); 21 } 22 monoSt.push(i); 23 } 24 25 //nums1按需从temp中取值 26 for(int i=0;i<len1;i++){ 27 ans[i]=temp[numMap[nums1[i]]]; 28 } 29 30 return ans; 31 } 32 };
3.下一个更大元素——可循环比较
3.1.问题描述
给定一个循环数组 nums ( nums[nums.length - 1] 的下一个元素是 nums[0] ),返回 nums 中每个元素的 下一个更大元素 。
数字 x 的 下一个更大的元素 是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1 。
链接:https://leetcode.cn/problems/next-greater-element-ii
3.2.要点
相较于温度问题,只是第一遍遍历之后仍有值在右侧找不到较大值,此时只需在左侧再遍历一遍
3.3.代码实例
1 class Solution { 2 public: 3 vector<int> nextGreaterElements(vector<int>& nums) { 4 int len=nums.size(); 5 vector<int> ret(len,-1); 6 7 stack<int> monoSt; 8 for(int i=0;i<len;i++){ 9 while(!monoSt.empty()&&nums[i]>nums[monoSt.top()]){ 10 ret[monoSt.top()]=nums[i]; 11 monoSt.pop(); 12 } 13 monoSt.push(i); 14 } 15 16 for(int i=0;i<len;i++){ 17 if(!monoSt.empty()&&i==monoSt.top()){ 18 break; 19 } 20 while(!monoSt.empty()&&nums[i]>nums[monoSt.top()]){ 21 ret[monoSt.top()]=nums[i]; 22 monoSt.pop(); 23 } 24 } 25 26 return ret; 27 } 28 };
4.接雨水
4.1.问题描述
给定 n
个非负整数表示每个宽度为 1
的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
链接:https://leetcode.cn/problems/trapping-rain-water/
4.2.要点
确定每根柱子上方能承载多少水,取决于左右两侧遇到的第一个高地中较矮的那个
4.3.代码实例
1 class Solution { 2 public: 3 int trap(vector<int>& height) { 4 int ret=0; 5 int len=height.size(); 6 7 int bottom=0,cover=0,width=0;; 8 stack<int> monoSt; 9 for(int i=0;i<len;i++){ 10 //当待入栈值大于栈顶值时 11 while(!monoSt.empty()&&height[i]>=height[monoSt.top()]) 12 { 13 //以栈顶值为底 14 bottom=height[monoSt.top()]; 15 monoSt.pop(); 16 17 if(monoSt.empty()){ 18 break; 19 } 20 21 //以两侧遇到的第一个较大值中较小的那个为顶 22 cover=min(height[monoSt.top()],height[i]); 23 24 //以左右距离为宽 25 width=i-monoSt.top()-1; 26 27 //计算面积 28 ret+=width*(cover-bottom); 29 } 30 monoSt.push(i); 31 } 32 33 return ret; 34 } 35 };
5.柱状图中可以圈出的最大面积
5.1.问题描述
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
链接:https://leetcode.cn/problems/largest-rectangle-in-histogram/
5.2.要点
和接雨水相反,这里要看每根柱子能找到的左右较小边界,以自身为高,边界距离为宽计算面积
5.3.代码实例
1 // 版本一 2 class Solution { 3 public: 4 int largestRectangleArea(vector<int>& heights) { 5 stack<int> st; 6 heights.insert(heights.begin(), 0); // 数组头部加入元素0 7 heights.push_back(0); // 数组尾部加入元素0 8 st.push(0);//入栈 9 10 int result = 0; 11 // 第一个元素已经入栈,从下标1开始 12 for (int i = 1; i < heights.size(); i++) { 13 // 注意heights[i] 是和heights[st.top()] 比较 ,st.top()是下标 14 if (heights[i] > heights[st.top()]) { 15 st.push(i); 16 } else if (heights[i] == heights[st.top()]) { 17 st.pop(); // 这个可以加,可以不加,效果一样,思路不同 18 st.push(i); 19 } else { 20 //发现当前栈顶元素的右边界 21 while (heights[i] < heights[st.top()]) { // 注意是while 22 int mid = st.top(); 23 st.pop(); 24 int left = st.top(); 25 int right = i; 26 int w = right - left - 1; 27 int h = heights[mid]; 28 result = max(result, w * h); 29 } 30 st.push(i); 31 } 32 } 33 return result; 34 } 35 };
xxx.问题
xxx.1.问题描述
111
xxx.2.要点
222
xxx.3.代码实例
333