刷题实践:最大矩形面积问题 | 豆包MarsCode AI刷题

31 阅读5分钟

题目🛫

题目内容

给定一个数组 array,包含 n 个元素,每个元素代表某种高度。任务是找出任意 k 个相邻元素所能形成的最大矩形面积。对于 k 个相邻元素,其矩形的最大面积定义为 R(k) = k * min(h[i], h[i+1], ..., h[i+k-1]),即 k 个元素的最小高度乘以 k。我们需要找出对于任意 kR(k) 的最大值。

题目分析

这个问题可以通过动态规划或者单调栈的方法来解决。动态规划的方法会涉及到状态转移方程的构建,而单调栈的方法则利用栈的特性来快速找到每个元素的左右边界,从而计算出以每个元素为最小高度的矩形面积。

思考与求解💡

在刚开始拿到这个题目的时候,我完全没有相关思路,于是我向豆包MarsCode AI进行询问,很快就得到了答复——单调栈!

image.png

单调栈(Monotonic Stack)是一种特殊的数据结构,它通常用于解决一些与数组或序列相关的问题,特别是那些需要找到某个元素的“前一个”或“后一个”特定关系的元素的问题。单调栈的核心思想是维护一个栈,使得栈中的元素保持单调递增或单调递减的顺序。

核心要点

核心在于利用单调栈的性质,通过两次遍历数组来分别确定每个元素的左侧和右侧边界,即找到每个元素左侧和右侧第一个比它高的元素的位置。然后,对于数组中的每个元素,计算以它为最小高度的矩形面积,即该元素的高度乘以它到左右边界的距离减一。最后,遍历所有元素,找出这些矩形面积中的最大值,即为所求的最大矩形面积。

  1. 单调栈的应用:使用两个栈来维护每个元素的左右边界。对于每个元素,我们可以通过栈来快速找到其左侧和右侧第一个比当前元素高的元素的索引,这样就能计算出以当前元素为最小高度的矩形面积。

  2. 左右边界的计算:首先计算每个元素的左侧边界,然后计算每个元素的右侧边界。左侧边界是指从左边开始,第一个比当前元素高的元素的位置;右侧边界是指从右边开始,第一个比当前元素高的元素的位置。

  3. 计算最大面积:对于每个元素,我们可以通过其左右边界的距离减去1(因为包括当前元素),再乘以当前元素的高度,得到一个矩形的面积。然后比较所有这样的矩形面积,找出最大的一个。

这种方法有效地避免了直接枚举所有子数组的低效,通过栈的辅助,实现了时间复杂度为 O(n) 的高效求解

核心代码🔖

核心代码主要涉及到两个部分:计算左右边界和计算最大面积。

# 计算每个位置左侧可以延伸到的下标
for i in range(n):
    while stk and array[stk[-1]] >= array[i]:
        stk.pop()
    left[i] = stk[-1] if stk else -1
    stk.append(i)

# 重置栈
stk = []
# 计算每个位置右侧可以延伸到的下标
for i in range(n-1, -1, -1):
    while stk and array[stk[-1]] >= array[i]:
        stk.pop()
    right[i] = stk[-1] if stk else n
    stk.append(i)

# 计算最大矩形面积
res = 0
for i in range(n):
    res = max(res, (right[i] - left[i] - 1) * array[i])

完整代码

def solution(n, array):
    # 使用列表模拟栈
    left = [-1] * n
    right = [n] * n
    stk = []

    # 计算每个位置左侧可以延伸到的下标
    for i in range(n):
        while stk and array[stk[-1]] >= array[i]:
            stk.pop()
        left[i] = stk[-1] if stk else -1
        stk.append(i)

    # 重置栈
    stk = []
    # 计算每个位置右侧可以延伸到的下标
    for i in range(n-1, -1, -1):
        while stk and array[stk[-1]] >= array[i]:
            stk.pop()
        right[i] = stk[-1] if stk else n
        stk.append(i)

    # 计算最大矩形面积
    res = 0
    for i in range(n):
        res = max(res, (right[i] - left[i] - 1) * array[i])

    return res

if __name__ == "__main__":
    # Add your test cases here
    print(solution(5, [1, 2, 3, 4, 5]) == 9)
    print(solution(6, [5, 4, 3, 2, 1, 6]) == 9)
    print(solution(4, [4, 4, 4, 4]) == 16)

这段代码首先计算了每个元素的左右边界,然后通过这些边界计算出以每个元素为最小高度的矩形面积,并找出最大的一个。这种方法的时间复杂度是 O(n),因为每个元素只被处理了两次(一次计算左侧边界,一次计算右侧边界)。

彩蛋:拓展应用😋

image.png

我发现豆包MarsCode AI在回复的时候,常常会告诉我们某个算法的其他应用场景。TA不仅关注算法本身,还致力于拓展我们的视野,让我们了解到算法在不同领域的广泛应用。

比如在这里的单调栈中,我们就可以看到单调栈之于最近最大(小)元素的一种解题思路

总结👍

运用豆包MarsCode AI智能刷题系统,我们可以在提问中学习,在学习中实践,在实践中思考,在思考中拓展。这种方式不仅有助于我们深入理解算法的本质,还能有效拓宽我们的算法视野,让算法学习之路更加高效和深远。