中等题:最大乘积问题

43 阅读4分钟

最大乘积问题

问题描述

小R最近遇到了一个数组问题。他有一个包含 N 个元素的数组,记作 a[1],a[2],…,a[N]。为了分析这个数组的特性,小R定义了两个函数 L(i) 和 R(i),并希望通过这两个函数来找到一些有趣的结论。

  • 函数 L(i) :表示在索引 i 之前的元素中,最后一个比 a[i] 大的元素的索引 j(即 j<i 且 a[j]>a[i])。如果找不到这样的 j,则 L(i)=0。如果有多个满足条件的 j,选择离 i 最近的那个。
  • 函数 R(i) :表示在索引 i 之后的元素中,第一个比 a[i] 大的元素的索引 k(即 k>i 且 a[k]>a[i])。如果找不到这样的 k,则 R(i)=0。如果有多个满足条件的 k,选择离 i 最近的那个。

最终,小R定义 MAX(i)=L(i)×R(i),他想知道在 1≤i≤N 的范围内,MAX(i) 的最大值是多少。

思路解析

为了高效地计算 ( L(i) ) 和 ( R(i) ),我们可以使用单调栈(Monotonic Stack)。这种数据结构可以帮助我们在遍历数组时,以线性时间复杂度找到所需的索引。

  1. 计算 ( L(i) )

    • 从左到右遍历数组,使用栈存储索引。对于每个元素,弹出栈顶元素,直到找到一个比当前元素大的元素。这个元素的索引即为 ( L(i) )。
  2. 计算 ( R(i) )

    • 从右到左遍历数组,使用相同的逻辑,找到第一个比当前元素大的元素的索引,作为 ( R(i) )。
  3. 计算最大乘积

    • 遍历所有 ( i ),计算 ( MAX(i) ) 并记录最大值。

步骤

  1. 初始化:创建两个数组 ( L ) 和 ( R ) 用于存储每个元素的 ( L(i) ) 和 ( R(i) ) 值,均初始化为 0。

  2. 计算 ( L(i) )

    • 使用一个空栈遍历数组。对于每个元素:
      • 如果栈不为空且栈顶元素小于等于当前元素,则弹出栈顶元素。
      • 如果栈不为空,记录当前元素的 ( L(i) ) 为栈顶元素的索引加 1;否则,记录为 0。
      • 将当前元素的索引压入栈中。
  3. 计算 ( R(i) )

    • 使用相同的逻辑,从右到左遍历数组,更新 ( R(i) )。
  4. 计算最大值

    • 遍历 ( L ) 和 ( R ) 数组,计算每个 ( MAX(i) ) 的值,并更新最大值。

图解

假设我们有一个数组 ( [5, 4, 3, 4, 5] )。

计算 ( L(i) )

  • i=0: L(0) = 0
  • i=1: L(1) = 1 (5 > 4)
  • i=2: L(2) = 1 (5 > 3)
  • i=3: L(3) = 1 (5 > 4)
  • i=4: L(4) = 3 (4 > 5)

最终 ( L = [0, 1, 1, 1, 3] )

计算 ( R(i) )

  • i=4: R(4) = 0
  • i=3: R(3) = 4 (5 > 4)
  • i=2: R(2) = 3 (4 > 3)
  • i=1: R(1) = 2 (4 > 4)
  • i=0: R(0) = 0

最终 ( R = [0, 2, 3, 4, 0] )

计算 ( MAX(i) )

  • ( MAX(0) = L(0) \times R(0) = 0 )
  • ( MAX(1) = L(1) \times R(1) = 1 \times 2 = 2 )
  • ( MAX(2) = L(2) \times R(2) = 1 \times 3 = 3 )
  • ( MAX(3) = L(3) \times R(3) = 1 \times 4 = 4 )
  • ( MAX(4) = L(4) \times R(4) = 3 \times 0 = 0 )

最终最大值为 8。

代码详解

以下是实现代码:

def solution(n, array):
    # 初始化L和R数组
    L = [0] * n
    R = [0] * n
    
    # 计算L(i)
    stack = []
    for i in range(n):
        while stack and array[stack[-1]] <= array[i]:
            stack.pop()
        if stack:
            L[i] = stack[-1] + 1  # +1因为题目要求从1开始
        else:
            L[i] = 0
        stack.append(i)
    
    # 计算R(i)
    stack = []
    for i in range(n - 1, -1, -1):
        while stack and array[stack[-1]] <= array[i]:
            stack.pop()
        if stack:
            R[i] = stack[-1] + 1  # +1因为题目要求从1开始
        else:
            R[i] = 0
        stack.append(i)
    
    # 计算最大值
    max_product = 0
    for i in range(n):
        max_product = max(max_product, L[i] * R[i])
    
    return max_product

if __name__ == "__main__":
    # 测试用例
    print(solution(5, [5, 4, 3, 4, 5]) == 8)  # 输出: True
    print(solution(6, [2, 1, 4, 3, 6, 5]) == 15)  # 输出: True
    print(solution(7, [1, 2, 3, 4, 5, 6, 7]) == 0)  # 输出: True

代码分析

  • L和R数组:分别存储每个元素的 ( L(i) ) 和 ( R(i) ) 值。
  • 单调栈:用于高效查找比当前元素大的元素的索引,保证时间复杂度为 ( O(N) )。
  • 最大乘积计算:遍历 ( L ) 和 ( R ) 数组,计算并更新最大乘积。

个人总结

通过这个问题,我深入理解了如何使用单调栈来解决与数组相关的最值问题。单调栈的应用不仅提高了算法的效率,还简化了代码的复杂度。在实际开发中,掌握这一技巧将极大地提升解决问题的能力。

最大乘积问题不仅考察了对数据结构的理解,还锻炼了逻辑思维能力。在今后的学习和工作中,我会继续关注数据结构的应用,力求在实际问题中找到更优雅的解决方案。