单调栈
-
单调递减栈:栈中数据出栈的序列为单调递减序列
模拟单调栈的数据push和pop
模拟实现一个递增单调栈:
-
10入栈时,栈为空,直接入栈,栈内元素为10。
-
3入栈时,栈顶元素10比3大,则入栈,栈内元素为10,3。
-
7入栈时,栈顶元素3比7小,则栈顶元素出栈,此时栈顶元素为10,比7大,则7入栈,栈内元素为10,7。
-
4入栈时,栈顶元素7比4大,则入栈,栈内元素为10,7,4。
-
12入栈时,栈顶元素4比12小,4出栈,此时栈顶元素为7,仍比12小,栈顶元素7继续出栈,此时栈顶元素为10,仍比12小,10出栈,此时栈为空,12入栈,栈内元素为12。
单调栈的伪代码
Stack<> stack = new Stack<>();
//此处一般需要给数组最后添加结束标志符,具体下面例题会有详细讲解
for (遍历这个数组)
{
if (栈空 || 栈顶元素大于等于当前比较元素)
{
入栈;
}
else
{
while (栈不为空 && 栈顶元素小于当前元素)
{
栈顶元素出栈;
更新结果;
}
当前数据入栈;
}
}
单调栈的应用
1.视野总和
描叙:有n个人站队,所有的人全部向右看,个子高的可以看到个子低的发型,给出每个人的身高,问所有人能看到其他人发型的总和是多少。 输入:4 3 7 1 输出:2 解释:个子为4的可以看到个子为3的发型,个子为7可以看到个子为1的身高,所以1+1=2 思路:观察题之后,我们发现实际上题目转化为找当前数字向右查找的第一个大于他的数字之间有多少个数字,然后将每个结果累计就是答案,但是这里时间复杂度为O(N^2),所以我们使用单调栈来解决这个问题。
1.设置一个单调递增的栈(栈内0~n为单调递减) 2.当遇到大于栈顶的元素,开始更新之前不高于当前人所能看到的值
public int findSum(int[] people) {
// 需要一个无限高的人使栈中的人出栈
people[people.length-1] = Integer.MAX_VALUE;
Stack<Integer> stack = new Stack<>();
int sum = 0;
for (int i = 0; i < people.length; i++) {
// 小于栈顶元素就入栈
if (stack.isEmpty() || people[stack.peek()] > people[i]){
stack.push(i);
} else {
while (!stack.isEmpty() && people[i] >= people[stack.peek()]){
int topIndex = stack.pop();
sum += (i-topIndex-1);
}
stack.push(i);
}
}
return sum;
}
2.柱状图中的最大矩形
https://leetcode-cn.com/problems/largest-rectangle-in-histogram/
public int largestRectangleArea(int[] heights) {
if (heights.length == 0) return 0;
int[] right = new int[heights.length];
Arrays.fill(right, heights.length);
Stack<Integer> rightStack = new Stack<>();
// 为每一个数字找到第一个比他小的数字的坐标,从左往右
for (int i = 0; i < heights.length; i++) {
// 如果新的数字比栈顶数字大,入栈
if (rightStack.isEmpty() || heights[i] >= heights[rightStack.peek()]){
rightStack.push(i);
} else {
// 如果新的数字比栈顶数字小,开始记录,并且弹出栈顶元素
while (!rightStack.isEmpty() && heights[i] < heights[rightStack.peek()]) {
int topIndex = rightStack.pop();
right[topIndex] = i;
}
rightStack.push(i);
}
}
int[] left = new int[heights.length];
Arrays.fill(left, -1);
Stack<Integer> leftStack = new Stack<>();
for (int i = heights.length-1; i >= 0; i--) {
if (leftStack.isEmpty() || heights[i] >= heights[leftStack.peek()]) {
leftStack.push(i);
} else {
while (!leftStack.isEmpty() && heights[i] < heights[leftStack.peek()]) {
int topIndex = leftStack.pop();
left[topIndex] = i;
}
leftStack.push(i);
}
}
int maxArea = Integer.MIN_VALUE;
for (int i = 0; i < heights.length; i++) {
int leftSide = left[i];
int rightSide = right[i];
int curArea = (rightSide - leftSide - 1)*heights[i];
maxArea = Math.max(maxArea, curArea);
}
return maxArea;