问题描述
小R最近遇到了一个数组问题。他有一个包含 NN 个元素的数组,记作 a1,a2,...,aNa1,a2,...,aN。为了分析这个数组的特性,小R定义了两个函数 L(i)L(i) 和 R(i)R(i),并希望通过这两个函数来找到一些有趣的结论。
- L(i)L(i) 是满足以下条件的最大的 jj 值:
- j<ij<i
- a[j]>a[i]a[j]>a[i]
- 如果找不到这样的 jj,那么 L(i)=0L(i)=0;如果有多个满足条件的 jj,选择离 ii 最近的那个。
- R(i)R(i) 是满足以下条件的最小的 kk 值:
- k>ik>i
- a[k]>a[i]a[k]>a[i]
- 如果找不到这样的 kk,那么 R(i)=0R(i)=0;如果有多个满足条件的 kk,选择离 ii 最近的那个。
最终,小R定义 MAX(i)=L(i)∗R(i)MAX(i)=L(i)∗R(i),他想知道在 1≤i≤N1≤i≤N 的范围内,MAX(i)MAX(i) 的最大值是多少。
测试样例
样例1:
输入:
n = 5, array = [5, 4, 3, 4, 5]
输出:8
样例2:
输入:
n = 6, array = [2, 1, 4, 3, 6, 5]
输出:15
样例3:
输入:
n = 7, array = [1, 2, 3, 4, 5, 6, 7]
输出:0
问题解析:
需要分析数组中的每个元素,找到它左右两侧第一个比它大的元素,并通过这两个元素的索引计算最大值。
关键点:
-
L(i) 的定义:
- 在当前位置
i左侧找到第一个比array[i]大的元素。 - 如果不存在,则
L(i) = 0。 - 否则,
L(i)是这个元素的索引(从1开始计数)。
- 在当前位置
-
R(i) 的定义:
- 在当前位置
i右侧找到第一个比array[i]大的元素。 - 如果不存在,则
R(i) = 0。 - 否则,
R(i)是这个元素的索引(从1开始计数)。
- 在当前位置
-
MAX(i) 的定义:
MAX(i) = L(i) * R(i)。- 最终要找到所有
MAX(i)中的最大值。
实现思路:
-
初始化数组
L和R:- 创建两个数组
L和R,分别存储每个位置的L(i)和R(i)。
- 创建两个数组
-
使用单调栈计算
L(i):- 遍历数组,对于每个元素,利用单调栈找到左侧第一个比当前元素大的值。
- 如果栈顶元素比当前元素小或相等,弹出栈顶元素。
- 栈顶元素即为左侧第一个比当前元素大的元素。
-
使用单调栈计算
R(i):- 从右向左遍历数组,逻辑类似计算
L(i)。 - 使用单调栈找到右侧第一个比当前元素大的值。
- 从右向左遍历数组,逻辑类似计算
-
计算
MAX(i)并求最大值:- 遍历所有位置,计算
MAX(i)并记录最大值。
- 遍历所有位置,计算
代码分析:
def solution(n, array):
# 初始化 L 和 R 数组
L = [0] * n # L[i] 存储第一个左侧比 array[i] 大的元素的索引
R = [0] * n # R[i] 存储第一个右侧比 array[i] 大的元素的索引
# 使用单调栈计算 L(i)
stack = [] # 存储索引的单调栈
for i in range(n):
# 移除栈中比当前元素小的元素
while stack and array[stack[-1]] <= array[i]:
stack.pop()
# 栈顶元素是第一个比 array[i] 大的左侧元素
L[i] = stack[-1] + 1 if stack else 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()
# 栈顶元素是第一个比 array[i] 大的右侧元素
R[i] = stack[-1] + 1 if stack else 0
stack.append(i)
# 计算 MAX(i) 的最大值
max_value = 0
for i in range(n):
max_value = max(max_value, L[i] * R[i])
return max_value
复杂度分析:
-
时间复杂度:
- 计算
L和R的过程各遍历数组一次,每个元素最多被压入和弹出栈一次,时间复杂度为 O(n)。 - 最终求
MAX(i)最大值的循环也是 O(n)。 - 总时间复杂度: O(n)。
- 计算
-
空间复杂度:
- 额外使用了两个数组
L和R,每个大小为 O(n)。 - 使用单调栈存储索引,栈的最大深度为 O(n)。
- 总空间复杂度: O(n)。
- 额外使用了两个数组
最大乘积问题解析
问题描述:
小R有一个包含 NNN 个元素的数组 a1,a2,...,aNa_1, a_2, ..., a_Na1,a2,...,aN。他定义了两个函数 L(i)L(i)L(i) 和 R(i)R(i)R(i):
- L(i)L(i)L(i):在当前位置 iii 左侧找到第一个比 a[i]a[i]a[i] 大的元素索引(从1开始)。如果没有,返回0。
- R(i)R(i)R(i):在当前位置 iii 右侧找到第一个比 a[i]a[i]a[i] 大的元素索引(从1开始)。如果没有,返回0。
最终定义 MAX(i)=L(i)×R(i)MAX(i) = L(i) \times R(i)MAX(i)=L(i)×R(i),要求找到所有 MAX(i)MAX(i)MAX(i) 中的最大值。
示例:
- 输入:
n = 5, array = [5, 4, 3, 4, 5] - 输出:
8
知识总结
-
单调栈:
- 用于在
O(N)时间复杂度内求解下一个更大/更小元素问题。 - 针对这个问题,单调栈可以帮助快速找到左侧和右侧第一个比当前元素大的索引。
- 用于在
-
优化思路:
- 采用单调递减栈分别从左到右和从右到左遍历数组,记录每个位置
L(i)和R(i)。 - 最终遍历数组,计算所有
MAX(i),取最大值。
- 采用单调递减栈分别从左到右和从右到左遍历数组,记录每个位置
学习计划
-
学习单调栈的原理及其应用场景,结合动态规划或滑动窗口,尝试解决复杂的栈应用题目。
-
对错题进行详细总结,分析错误原因,记录解法和注意点。
工具运用
- 错题分析: 使用 豆包 AI 生成的详细解析帮助理解错题,能够快速找到解题关键点。
- 知识学习: 通过 豆包 AI 解析题目后,遇到不懂的概念,也可以结合其他学习资料去学习并理解。