求两侧最近的更小点,用单调栈解Largest Rectangle in Histogram和trapping-rain-water

LeetCode经典算法解析
本文深入解析LeetCode上的经典算法问题,如LargestRectangleinHistogram和trapping-rain-water,介绍如何使用单调栈求解左右边界的问题,以及动态规划和最小栈的应用。文章通过实例代码详细阐述了算法背后的逻辑,帮助读者理解并掌握这些高效算法。

Largest Rectangle in Histogram参考了两种解法,最后用以中间的某点为高度,快速分别求左右边最近的比它低的点为整体思路(编者更新注:推荐直接按此思路实现,因为更容易,ac代码),以单调栈单调栈用最大/小栈实现,可实现在O(n)时间内获取所有点的左右边最近的更小/大点(严格来说应是左侧的更不大/不小点,右侧的更小/大点)的功能)为实现方式,实现了:

	public int largestRectangleArea(int[] heights) {
		int max = 0;
		Stack<Integer> maxst = new Stack<Integer>() { //简化版最大栈,直观表现为单调不递减数列
			{
				push(-1);
			}
		};
		for (int i = 0; i <= heights.length; ++i) {
			int righth = i == heights.length ? 0 : heights[i];
			int midi = maxst.peek();
			int midh = midi==-1 ? 0 : heights[midi];
			if(righth>=midh) {
				maxst.push(i);
			}else{
				maxst.pop();  //对应方法一的中间值的索引i
				int area = midh*(i-maxst.peek()-1); /*i对应方法一的lessFromRight[i], maxst.peek()对应方法一的lessFromLeft[i]*/
				max = Math.max(max, area);
				--i;
			}
		}
		return max;
	}

前种方法可读性强,但是我没明白为什么是O(n),评论里有句“This process of calculate lessFromLeft can be seen as a process of push stack and pop stack, so each element will only be accessed twice.” 但是我也没看明白怎么就像栈的。倒是单调栈的方法,代码更简单,虽然我没看到有谁对它的含义有清晰的解释,但它的意义却可以用前种方法完美地来解释,可见于我在上面代码的注释

trapping-rain-water思路一(推荐):用动态规划快速求得某点的左右两侧的最大值,对应官方解中的Approach 2,ac代码
思路二:可运用最小栈来实现,思想是已知中间某点,快速分别求左右边最近的比它高的点,ac的代码:(或者)

    public int trap(int[] height) {
        if(height.length<=2)
            return 0;
		Stack<Integer> minst = new Stack<Integer>() ;
		int i = 0;
		for (; i < height.length; ++i) {
			if(height[i]!=0) {
				minst.push(i);
				break;
			}
		}
		int trap = 0;
		for (++i; i < height.length; ++i) {
			int righth = height[i];
			int midi = minst.peek();
			int midh = height[midi];
			if(righth<=midh) {
				minst.push(i);
			}else{
				minst.pop();
				if(!minst.isEmpty()) {
					int heightdif = Math.min(righth, height[minst.peek()])-midh;
					trap += heightdif*(i-minst.peek()-1);
					--i;
				}else {
					minst.push(i);
				}
			}
		}
		return trap;
    }

单调递增栈为什么能找左边最近的更小值

Editorial of LeetCode907. Sum of Subarray Minimums :

To build a monotonically increasing stack, as we iterate through an array’s elements, we push them onto the stack. But before pushing an element, we ensure that always increasing property is maintained.
This means if the item at the top of the stack is bigger than or equal to the current item under iteration, we first pop it off before pushing the current element on top.

As a new item gets added to the stack, older items are removed from the top if they are bigger. In other words, the items that are getting popped must be greater than or equal to the incoming element. We can also say that the incoming element must be the next smaller element of the item going off the stack. So, every time an item is popped, we get to know about its next smaller item.
If the stack is not empty, the new stack top would contain the previous smaller item. That’s because whenever a new item is added to the stack, we ensure that all the bigger items are removed first. So the stack guarantees that the previous element has to be the previous smaller element.
If the stack becomes empty at the time of removal of an item, it indicates that the outgoing item is the smallest item seen so far. Also note that once the process is complete, the stack contains a series of items sorted in increasing order. These are also the items that have no smaller items after themselves. And their previous smaller items are stored right below them in the stack.

此题的扩展题目:
https://leetcode.com/problems/product-of-array-except-self/
https://leetcode.com/problems/trapping-rain-water-ii/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

qq_23204557

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

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

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

打赏作者

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

抵扣说明:

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

余额充值