Python Leetcode求解柱状图中最大的矩形

题目如下:
在这里插入图片描述
1.暴力求解

面积 = 底 * 高:
1.固定底,求最大高度;
2.固定高,求最长底边。采用双指针从中间指针向两边扩散,直到找到严格小于中间指针所对应的高度。

  • 如果我们枚举「宽」,我们可以使用两重循环枚举矩形的左右边界以固定宽度,此时矩形的高度就是所有包含在内的柱子的「最小高度」,代码如下:
class Solution:
    def largestRectangleArea(self, heights: List[int]) -> int:
        n = len(heights)
        res = 0
        for i in range(n):
            for j in range(i, n):
                res = max(res, ((j - i + 1) * min(heights[i:j+1])))
        return res
  • 如果我们枚举「高」,我们可以使用一重循环枚举某一根柱子,将其固定为矩形的高度h。随后我们从这跟柱子开始向两侧延伸,直到遇到高度小于h的柱子,就确定了矩形的左右边界。代码如下:
class Solution:
    def largestRectangleArea(self, heights: List[int]) -> int:
        n = len(heights)
        res = 0
        for i in range(n):
            l, r = i, i
            current = heights[i]
            while l >= 0 and heights[l] >= current:
                l -= 1
            while r <= n - 1 and heights[r] >= current :
                r += 1
            res= max(res, (r - l - 1) * current)
        return res

暴力求解的时间复杂度为O(n2)。考虑到枚举「宽」的方法使用了两重循环,本身就已经需要 O(N2)的时间复杂度,不容易优化,因此我们可以考虑优化只使用了一重循环的枚举「高」的方法。
空间换时间,因此可以得到的数据结构是栈。

2.单调栈

将数组的索引入栈,当当前索引对应的数值小于栈顶索引对应的数值时,栈顶元素出栈,可以确定该索引对应下的数值下的最大面积。

class Solution:
    def largestRectangleArea(self, heights: List[int]) -> int:
        n = len(heights)
        stack = []
        res = 0
        for i in range(n):
            while stack and heights[i] < heights[stack[-1]]:
                cur_h = heights[stack.pop()]

                while stack and cur_h == heights[stack[-1]]:
                    stack.pop()
                
                if stack:
                    cur_w = i - stack[-1] - 1
                else:
                    cur_w = i
                
                res = max(res, cur_h * cur_w)
            stack.append(i)

        while stack:
            cur_h = heights[stack.pop()]
            while stack and cur_h == heights[stack[-1]]:
                stack.pop()
            if stack:
                cur_w = n - stack[-1] - 1
            else:
                cur_w = n
            res = max(res, cur_h * cur_w)

        return res

时间复杂度O(N):输入数组里的每一个元素入栈一次,出栈一次;
空间复杂度O(N):栈的空间最大为数组的长度。

3.单调栈+常数优化

在方法2中,我们遍历完N次循环后,还进行了一次栈空间元素排查,是否还存在索引值,这是因为经历N次循环后,入栈的元素并不一定能够全部出栈,因此,如果我们在数组的开头和结尾各加一个很小的数(如0),则N次循环后,栈可以变为空。该两头添加元素的方法称为哨兵方法。另外,如果有连续的数值相等,在方法2中进行了一个while循环进行连续出栈操作,实际上不进行该操作,结果也正确。
优化的代码如下:

class Solution:
    def largestRectangleArea(self, heights: List[int]) -> int:
        heights = [0] + heights + [0]   #在数组两头各加入一个哨兵0
        n = len(heights)
        stack = [0]     #将数组头的哨兵入栈,因此栈可以不用做非空判断
        res = 0

        for i in range(1, n):
            while heights[i] < heights[stack[-1]]:
                cur_h = heights[stack.pop()]
                cur_w = i - stack[-1] - 1
                res = max(res, cur_h * cur_w)
            stack.append(i)
        return res
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值