單調棧
單調棧簡介
單調棧本質就是棧,其元素保持單調。單調棧實際上就是棧,只是利用了一些巧妙的邏輯,使得每次新元素入棧後,棧內的元素都保持有序(單調遞增或單調遞減)
- 從棧底到棧頂,依次遞減的是單調遞減棧。
- 從棧底到棧頂,依次遞增的是單調遞增棧。
構造單調棧,可以用於解決陣列中元素右側下一個更大(更小)元素,或者左側下一個更大(更小)元素的一類問題。例如Leetcode中的Q84,Q85,Q496,Q503,Q739等。
單調棧的構造
如果是下一個元素,即是求一個元素的右側滿足第一個條件的是倒序遍歷陣列
是左側的話就是順序遍歷陣列。
如果是找更大的元素就是單調遞減棧
更小的元素是單調遞增棧。
程式碼模板如下:
求解陣列中右側下一個更小元素的程式碼
public int[] increasing(int[] nums){
Deque<Integer> stack=new LinkedList<>();
for (int i = nums.length-1; i >=0; i--) {
//小於棧頂元素,彈出
while (!stack.isEmpty()&&nums[i]<stack.peek())
{
stack.pop();
}
//中間根據實際問題插入具體的操作
//大於棧頂就將此時的陣列元素入棧
stack.push(nums[i]);
}
return nums;
}
求解陣列中右側下一個更大元素的程式碼
public int[] decreasing(int[] nums){
Deque<Integer> stack=new LinkedList<>();
for (int i = nums.length-1; i >=0 ; i--) {
while (!stack.isEmpty()&&nums[i]>stack.peek())
{
stack.pop();
}
//中間根據實際問題插入具體的操作
stack.push(nums[i]);
}
return nums;
}
求解陣列中左側下一個更小元素的程式碼
public int[] increasing(int[] nums){
Deque<Integer> stack=new LinkedList<>();
for (int i =0; i <=nums.length-1; i++) {
//小於棧頂元素,彈出
while (!stack.isEmpty()&&nums[i]<stack.peek())
{
stack.pop();
}
//中間根據實際問題插入具體的操作
//大於棧頂就將此時的陣列元素入棧
stack.push(nums[i]);
}
return nums;
}
求解陣列中左側下一個更大元素的程式碼
public int[] increasing(int[] nums){
Deque<Integer> stack=new LinkedList<>();
for (int i = 0; i <=nums.length-1; i++) {
//小於棧頂元素,彈出
while (!stack.isEmpty()&&nums[i]>stack.peek())
{
stack.pop();
}
//中間根據實際問題插入具體的操作
//大於棧頂就將此時的陣列元素入棧
stack.push(nums[i]);
}
return nums;
}
典型例子
1.84. 柱狀圖中最大的矩形 - 力扣(LeetCode) (leetcode-cn.com)
求柱狀圖中的最大矩形。一個容易想到的演算法就是,從一個矩形向左/向右看,如果值大於自己,則這個矩形可以被包含,可以延展過來,找到第一個小於自己的矩形就是向兩側延展的最多的長度。暴力求解O(n^2)的時間複雜度,無法通過,超時。使用單調棧求出兩個陣列來儲存原來陣列中左側和右側第一個小於自己元素的位置便可以在O(n)時間複雜度下完成求解。
package JavaCode.leetcode.DataStructure.StackandQueue.Monotonous_stack;
import java.util.*;
import java.util.zip.InflaterInputStream;
/*
求解矩形的最大面積
可以通過構造倒序下一個最小值
逆序,單調遞增棧
*/
public class Q84 {
public static int largestRectangleArea(int[] heights) {
//求出所給陣列右側第一個更小元素的位置
Deque<Integer> stack1=new LinkedList<>();
int[] left=new int[heights.length];
for (int i = 0; i <= heights.length-1 ; i++) {
while (!stack1.isEmpty()&&heights[i]<=heights[stack1.peek()]){
stack1.pop();
}
//存在更小值,就將其位置取出,不存在取-1
left[i]=(stack1.isEmpty())?-1:stack1.peek();
stack1.push(i);
}
//求出所給陣列左側第一個更小元素的位置
Deque<Integer> stack2=new LinkedList<>();
int[] right=new int[heights.length];
for (int i = heights.length-1; i >=0 ; i--) {
while (!stack2.isEmpty()&&heights[i]<=heights[stack2.peek()]){
stack2.pop();
}
//存在更小值,就將其位置取出,不存在取-1
right[i]=stack2.isEmpty()?-1:stack2.peek();
stack2.push(i);
}
//遍歷每個矩形,求出其能夠構造的矩形往左右延申的長度,進而求出矩形面積,找到最大矩形。
int max=0,len= heights.length;
for (int i = 0; i < len; i++) {
int leftIndex=(left[i]==-1)?0:left[i]+1;
int rightIndex=(right[i]==-1)?len-1:right[i]-1;
int area=heights[i]*(rightIndex-leftIndex+1);
max=Math.max(max,area);
}
return max;
}
//求出給定元素左側第一個小於自己的元素的位置
}