最大乘积问题
问题描述
小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)。这种数据结构可以帮助我们在遍历数组时,以线性时间复杂度找到所需的索引。
-
计算 ( L(i) ):
- 从左到右遍历数组,使用栈存储索引。对于每个元素,弹出栈顶元素,直到找到一个比当前元素大的元素。这个元素的索引即为 ( L(i) )。
-
计算 ( R(i) ):
- 从右到左遍历数组,使用相同的逻辑,找到第一个比当前元素大的元素的索引,作为 ( R(i) )。
-
计算最大乘积:
- 遍历所有 ( i ),计算 ( MAX(i) ) 并记录最大值。
步骤
-
初始化:创建两个数组 ( L ) 和 ( R ) 用于存储每个元素的 ( L(i) ) 和 ( R(i) ) 值,均初始化为 0。
-
计算 ( L(i) ):
- 使用一个空栈遍历数组。对于每个元素:
- 如果栈不为空且栈顶元素小于等于当前元素,则弹出栈顶元素。
- 如果栈不为空,记录当前元素的 ( L(i) ) 为栈顶元素的索引加 1;否则,记录为 0。
- 将当前元素的索引压入栈中。
- 使用一个空栈遍历数组。对于每个元素:
-
计算 ( R(i) ):
- 使用相同的逻辑,从右到左遍历数组,更新 ( R(i) )。
-
计算最大值:
- 遍历 ( 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 ) 数组,计算并更新最大乘积。
个人总结
通过这个问题,我深入理解了如何使用单调栈来解决与数组相关的最值问题。单调栈的应用不仅提高了算法的效率,还简化了代码的复杂度。在实际开发中,掌握这一技巧将极大地提升解决问题的能力。
最大乘积问题不仅考察了对数据结构的理解,还锻炼了逻辑思维能力。在今后的学习和工作中,我会继续关注数据结构的应用,力求在实际问题中找到更优雅的解决方案。