单调栈

通过使用栈这个简单的结构,我们可以巧妙地降低一些问题的时间复杂度。

单调栈性质:

1、若是单调递增栈,则从栈顶到栈底的元素是严格递增的。若是单调递减栈,则从栈顶到栈底的元素是严格递减的。

2、越靠近栈顶的元素越后进栈。(显而易见)

本文介绍单调栈用法

通过一道题来说明。

POJ2559

1. 题目大意:链接

给出一个柱形统计图(histogram), 它的每个项目的宽度是1, 高度和具体问题有关。 现在编程求出在这个柱形图中的最大面积的长方形。

7 2 1 4 5 1 3 3

7表示柱形图有7个数据,分别是 2 1 4 5 1 3 3, 对应的柱形图如下,最后求出来的面积最大的图如右图所示。

做法1:枚举每个起点和终点,矩形面积就是长*最小高度。O(N^3)

做法2:区间最小值优化。O(N^2)

做法3:以每一个下标为中心向两边扩,遇到更短的就停,这样我们可以确定以每一个下标高度为最高的矩形。O(N^2)

单调栈:维护一个单调递增栈,所有元素各进栈和出栈一次即可。每个元素出栈的时候更新最大的矩形面积。

过程:

1)判断当前元素小于栈顶

2)条件满足,就可以更新栈顶元素的最大长度了,并且把栈顶弹出

3)继续执行(1),直到条件不满足。

 

重要结论:

1)栈顶下面一个元素一定是,栈顶左边第一个比栈顶小的元素

2)当前元素一定是,右边第一个比栈顶小的元素。

为什么呢?

比如这是个栈

1)如果右边存在距离更近的比1号小的数,1号早已经弹出了。

2)如果左边有距离更近的比1号小的数

                如果它比2号小,它会把2号弹出,自己成为2号

                 如果它比2号大,它不会弹出2号,但是它会压栈,变成2号,原来的2号成为3号。

所以不管怎么说,这个逻辑是正确的。

最后放代码并讲解

 

下面看一道难一些的题

LeetCode 85 Maximal Rectangle

1 0 1 0 0

1 0 1 1 1

1 1 1 1 1

1 0 0 1 0

Return 6.二三行后面那六个1

 

给定一个由二进制组成的矩阵map,找到仅仅包含1的最大矩形,并返回其面积。

这道题是一行一行的做。对每一行都求出每个元素对应的高度,这个高度就是对应的连续1的长度,然后对每一行都更新一次最大矩形面积。

连续1长度也很好更新,本个元素是0,长度就是0,本个元素是1,那就加上之前的。

具体思路代码中讲解。

import java.util.Stack;

public class MaximalRectangle {

	public static int maxRecSize(int[][] map) {
		if (map == null || map.length == 0 || map[0].length == 0) {
			return 0;
		}
		int maxArea = 0;
		int[] height = new int[map[0].length];
		for (int i = 0; i < map.length; i++) {
			for (int j = 0; j < map[0].length; j++) {
				height[j] = map[i][j] == 0 ? 0 : height[j] + 1;//0长度为0,1长度为前面+1
			}
			maxArea = Math.max(maxRecFromBottom(height), maxArea);//调用第一题的思想
		}
		return maxArea;
	}

	//第一题思路
	public static int maxRecFromBottom(int[] height) {
		if (height == null || height.length == 0) {
			return 0;
		}
		int maxArea = 0;
		Stack<Integer> stack = new Stack<Integer>();
		for (int i = 0; i < height.length; i++) {
                //栈非空并且栈顶大
			while (!stack.isEmpty() && height[i] <= height[stack.peek()]) {
				int j = stack.pop();//弹出
				int k = stack.isEmpty() ? -1 : stack.peek();
				int curArea = (i - k - 1) * height[j];//计算最大
				maxArea = Math.max(maxArea, curArea);//更新总体最大
			}
			stack.push(i);//直到栈顶小,压入新元素
		}
		//最后栈非空,右边没有更小元素使它们弹出
		while (!stack.isEmpty()) {
			int j = stack.pop();
			int k = stack.isEmpty() ? -1 : stack.peek();
			int curArea = (height.length - k - 1) * height[j];
			maxArea = Math.max(maxArea, curArea);
		}
		return maxArea;
	}

	public static void main(String[] args) {
		int[][] map = { { 1, 0, 1, 1 }, { 1, 1, 1, 1 }, { 1, 1, 1, 0 }, };
		System.out.println(maxRecSize(map));
	}

}

 

 

  • 1030
    点赞
  • 233
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 208
    评论
单调栈是一种常见的数据结构,在解决一些特定问题时非常有用。它可以用来求解一些范围内的最大值、最小值、最长连续递增子序列等问题。下面是一个简单的 Python 实现: ``` class MonotonicStack: def __init__(self): self.stack = [] def push(self, val): while self.stack and self.stack[-1] < val: self.stack.pop() self.stack.append(val) def pop(self): self.stack.pop() def top(self): return self.stack[-1] def is_empty(self): return len(self.stack) == 0 ``` 这个单调栈实现了四个基本操作: - push(val):将元素 val 压入栈中。在压入之前,如果栈顶元素比 val 小,则弹出栈顶元素,直到栈顶元素比 val 大或栈为空,然后将 val 压入栈中。 - pop():弹出栈顶元素。 - top():返回栈顶元素。 - is_empty():判断栈是否为空。 使用单调栈的时候,需要根据具体问题来实现 push 操作。下面是一个例子,求解一个数组中每个元素右边第一个比它大的数: ``` def next_larger(nums): n = len(nums) res = [-1] * n stack = MonotonicStack() for i in range(n): while not stack.is_empty() and nums[stack.top()] < nums[i]: res[stack.top()] = nums[i] stack.pop() stack.push(i) return res ``` 在这个例子中,我们维护了一个单调递减的栈,每次遇到一个比栈顶元素大的数,就将栈顶元素弹出,并将栈顶元素的答案设为当前数。这样,最后栈中剩下的元素都没有右边比它们大的数,它们的答案就是 -1。时间复杂度为 O(n)。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 208
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

兔老大RabbitMQ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值