leetcode专题训练 84. Largest Rectangle in Histogram

1. 方法一:代码比较多

若一个位置的高度为 h 1 h_1 h1在栈内,那么就说明后面有可能出现高为 h 1 h_1 h1而宽度更大的矩形,如果不在栈内,那么就说明后面不可能出现高为 h 1 h_1 h1而宽度更大的矩形。
可以通过以下方式判断有没有可能出现高为 h 1 h_1 h1而宽度更大的矩形:
如果 h 1 h_1 h1右边的高度 h 2 h_2 h2 h 1 h_1 h1要小,那么右边界就卡死了,高 h 1 h_1 h1的矩形的宽度就不能更大了,高 h 1 h_1 h1的最大面积矩形已经确定,就可以将 h 1 h_1 h1弹出栈,同时计算面积。
然而如果出现了 h 1 > h 2 h_1>h_2 h1>h2这种右边比左边小的情况,不仅能判断有没有可能出现高为 h 1 h_1 h1而宽度更大的矩形,还能判断是否能出现高为 h ( h 2 < h < h 1 ) h(h_2 < h< h_1) h(h2<h<h1)的宽度更大矩形。因为 h 2 h_2 h2将右边界卡死了,所以 h h h高度的在栈内的高度矩形最大面积也可确定了。就可以将这些也弹出栈,同时计算面积。
当全部 h ( h 2 < h < h 1 ) h(h_2 < h< h_1) h(h2<h<h1)索引弹出栈后,可以将 h 2 h_2 h2索引压入栈。
接下来介绍如何求已经不能更宽的矩形面积,也就是高度为弹出的高度 h ( h 2 < h < h 1 ) h(h_2 < h< h_1) h(h2<h<h1)的最宽矩形面积。最大面积为 h × ( 右 边 界 − 左 边 界 ) h\times(右边界-左边界) h×(),其中 右 边 界 = h 2 索 引 − 1 右边界=h_2索引-1 =h21 左 边 界 = 弹 出 h 后 栈 顶 索 引 + 1 左边界=弹出h后栈顶索引+1 =h+1。因为这样维护出的栈是一个递增栈,所以弹出 h h h后,栈顶的高度一定小于等于 h h h,也就是左边界卡死了。

然而需要了解到,高度数组最右边的高度需要特殊处理,因为其没有右边高度了。处理方式是如果扫到最右边的高度,则默认按照该高度右边有一个高度更低的高度来处理,因为不可能再出现宽度更大的矩形了。
还需要了解到,在考察左边界的时候,栈可能为空,这时候就可以将0作为左边界。因为当前矩形的高度一定左边的任何高度都要低,不然不会最后一个弹出栈。

完整代码:

class Solution:
    def largestRectangleArea(self, heights: List[int]) -> int:
        # 特殊情况判断
        n = len(heights)
        if n == 0:
            return 0
        if n == 1:
            return heights[0]

        result = 0
        stack = [0]
        for i in range(1, n):
            right = i-1
            # 右边界卡死
            while stack and heights[i] < heights[stack[-1]]:
                h1 = heights[stack.pop()]
                # 左边界
                left = 0
                if stack:
                    left = stack[-1]+1
                result = max(result, (right-left+1)*h1)

            stack.append(i)
        
        right = n-1
        # 处理最右边高度
        while stack:
            h1 = heights[stack.pop()]
            # 左边界
            left = 0
            if stack:
                left = stack[-1]+1
            result = max(result, (right-left+1)*h1)

        return result
2. 方法二:方法一进阶版,加上了“哨兵”,使得代码更简洁

在方法1中,因为边界问题,处理了两种特殊情况。所以在高度数组的最开始位置和最后位置分别加上一个0高度元素当作哨兵,这样栈就不会为空,且高度数组中的最后一个元素也能一起处理,就不用处理特殊情况了。

完整代码:

class Solution:
    def largestRectangleArea(self, heights: List[int]) -> int:
        # 特殊情况判断
        n = len(heights)
        if n == 0:
            return 0
        if n == 1:
            return heights[0]

        # 最左哨兵
        heights.insert(0,0)
        # 最右哨兵
        heights.append(0)
        n += 2

        result = 0
        stack = [0]
        for i in range(1, n):
            right = i-1
            # 右边界卡死
            while heights[i] < heights[stack[-1]]:
                h1 = heights[stack.pop()]
                # 左边界
                left = stack[-1]+1
                result = max(result, (right-left+1)*h1)

            stack.append(i)
        
        return result

最后不死心的附上一份超时的O( n 2 n^2 n2)暴力代码。过不了,别试了。

class Solution:
    def largestRectangleArea(self, heights: List[int]) -> int:
        result = 0
        n = len(heights)
        if n == 0:
            return 0
        minNum = [[0 for i in range(n)] for j in range(n)]
        for i in range(n):
            minNum[i][i] = heights[i]
            tmpMax = minNum[i][i]
            for j in range(i+1, n):
                minNum[i][j] = min(minNum[i][j-1], heights[j])
                tmpMax = max(tmpMax, minNum[i][j]*(j-i+1))
                result = max(result, tmpMax)
                # 如果当前区间最小数乘上从i到n的长度,都比当前的最优解要小,那么剪枝
                if minNum[i][j]*(n-i+1) < result:
                    break
            result = max(result, tmpMax)
            
        return result
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值