分析:

核心思想:欲求面积最大的矩形,可以确定该矩形的高度一定是 heights 数组中的元素。然后就可以枚举每个柱子的高度,找到每个柱子左右首个低于它的柱子的位置。

假设 h = heights[i] 是矩形的高度,其左右边界,即宽度最大是多少?

  • 在 i 左侧的小于 heights[i] 的最近元素的下标 left,如果不存在则为 -1。此时 left + 1 就是矩形最左边的那根柱子。
  • 在 i 右侧的小于 heights[i] 的最近元素的下标 right,如果不存在则为 n。此时 right – 1 就是矩形最右边的那根柱子。

因此矩形宽度为 right – 1 – (left + 1) + 1 = right – left – 1。因此最终的矩形面积为 h * (right – left – 1)。

题目给出的示例,可以得出选择 i = 2 这个柱子作为矩形的高,左边小于 heights[2] = 5 的最近元素的下标 left = 1,右边小于 heights[2] = 5 的最近元素的下标 right = 4,所以矩形的宽度是 right – left – 1 = 2,矩形面积为 h * (right – left – 1) = 5 * 2 = 10。

然后枚举出所有 i 对应矩形面积,取其最大为最终结果。

实现代码如下:

func largestRectangleArea(heights []int) int {
    n := len(heights)
    
    // 确定每个h对应的最左边的那根柱子, left + 1
    left := make([]int, n)
    st := []int{-1}
    for i, h := range heights {
        for len(st) > 1 && h <= heights[st[len(st) - 1]] {
            st = st[:len(st) - 1]
        }
        left[i] = st[len(st) - 1]
        st = append(st, i)
    }
    
    // 确定每个h对应的最右边的那根柱子,right - 1
    right := make([]int, n)
    st = st[:1]
    st[0] = n
    for i, h := range slices.Backward(heights) {
        for len(st) > 1 && h <= heights[st[len(st) - 1]] {// 这里heights[len(st) - 1]表示的是当前遍历的h的右边的数字
            st = st[:len(st) - 1]
        }
        right[i] = st[len(st) - 1]
        st = append(st, i)
    }

    // 遍历整个heights求出最大面积
    // 宽度为 right - 1 - (left + 1) + 1 = right - left - 1
    var result int
    for i, h := range heights {
        result = max(result, h * (right[i]- left[i] - 1))
    }

    return result
}

解析:

在求左边柱子时定义的 st := []{-1},本身目的是作为一个栈,初始化为 [-1] 。在循环中,对于每个索引 i 和高度 h,用来检查栈中是否不止有 -1 (len(st) > 1)且当前高度 h 小于等于栈顶索引对应的高度。如果是,则弹出栈顶。然后,将当前 left[i] 设置为栈顶索引(st[len(st) – 1]),并将当前索引 i 压入栈。弹栈的操作要一直进行直到不满足条件才可以停止。

left := make([]int, n)
st := []int{-1}
  for i, h := range heights {
      for len(st) > 1 && h <= heights[st[len(st) - 1]] {
          st = st[:len(st) - 1]
      }
      left[i] = st[len(st) - 1]
      st = append(st, i)
}

这就是上述代码的核心思想。

同理,求右边柱子时定义的 st := [] {n}。但是需要注意的是,再求左边柱子的时候,h 比较的是左侧柱子,因此是按 heights 的索引升序遍历。那么再求右边柱子的时候,h 比较的是右侧柱子,需要按 heights 的索引降序遍历。这里用到了 go 语言的基础库 slices 包下的 backward 方法,可以直接反向遍历数组,非常的方便。

参考链接:https://pkg.go.dev/slices#Backward

最后,遍历整个 heights 数组,结合前面求得的 left 和 right 数组,计算最大矩形面积即可。

复杂度分析:

使用三次遍历的方式来求解改题目

  • 时间复杂度:O(n),其中 n 是 heights 的长度。每个元素入栈和出栈都至多是一次,所以是 O(n) 的。
  • 空间复杂度:O(n)。

本体思路来源于:https://leetcode.cn/problems/largest-rectangle-in-histogram/solutions/2695467/dan-diao-zhan-fu-ti-dan-pythonjavacgojsr-89s7/?envType=study-plan-v2&envId=top-100-liked

Categories:

Tags:

No responses yet

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注