题目:最大乘积问题
问题描述
小R最近遇到了一个数组问题。他有一个包含 个元素的数组,记作 。为了分析这个数组的特性,小R定义了两个函数 和 ,并希望通过这两个函数来找到一些有趣的结论。
-
是满足以下条件的最大的 值:
- 如果找不到这样的 ,那么 ;如果有多个满足条件的 ,选择离 最近的那个。
-
是满足以下条件的最小的 值:
- 如果找不到这样的 ,那么 ;如果有多个满足条件的 ,选择离 最近的那个。
最终,小R定义 ,他想知道在 的范围内, 的最大值是多少。
测试样例
样例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
问题理解
-
给定一个包含
N个元素的数组array。 -
定义两个函数
L(i)和R(i):L(i)是满足j < i且array[j] > array[i]的最大的j值,如果找不到这样的j,则L(i) = 0。R(i)是满足k > i且array[k] > array[i]的最小的k值,如果找不到这样的k,则R(i) = 0。
-
定义
MAX(i) = L(i) * R(i),目标是找到MAX(i)的最大值。
数据结构选择
- 数组:用于存储输入的数组元素。
- 栈:用于高效地找到
L(i)和R(i)。栈可以帮助我们在遍历数组时,快速找到左边或右边第一个比当前元素大的元素。
算法步骤
-
初始化:
- 创建两个数组
L和R,分别用于存储每个元素的L(i)和R(i)。 - 创建一个栈,用于辅助计算
L(i)和R(i)。
- 创建两个数组
-
计算
L(i):-
从左到右遍历数组,对于每个元素
array[i]:- 如果栈不为空且栈顶元素对应的数组值小于等于
array[i],则弹出栈顶元素。 - 如果栈为空,则
L[i] = 0;否则,L[i] = 栈顶元素的索引 + 1。 - 将当前元素的索引入栈。
- 如果栈不为空且栈顶元素对应的数组值小于等于
-
-
计算
R(i):-
从右到左遍历数组,对于每个元素
array[i]:- 如果栈不为空且栈顶元素对应的数组值小于等于
array[i],则弹出栈顶元素。 - 如果栈为空,则
R[i] = 0;否则,R[i] = 栈顶元素的索引 + 1。 - 将当前元素的索引入栈。
- 如果栈不为空且栈顶元素对应的数组值小于等于
-
-
计算
MAX(i):- 遍历数组,计算
MAX(i) = L[i] * R[i],并记录最大值。
- 遍历数组,计算
代码实现
import java.util.Stack;
public class Main {
public static int solution(int n, int[] array) {
// 初始化 L 和 R 数组
int[] L = new int[n];
int[] R = new int[n];
// 使用栈来计算 L(i)
Stack<Integer> stack = new Stack<>();
for (int i = 0; i < n; i++) {
while (!stack.isEmpty() && array[stack.peek()] <= array[i]) {
stack.pop();
}
L[i] = stack.isEmpty() ? 0 : stack.peek() + 1;
stack.push(i);
}
// 清空栈,用于计算 R(i)
stack.clear();
for (int i = n - 1; i >= 0; i--) {
while (!stack.isEmpty() && array[stack.peek()] <= array[i]) {
stack.pop();
}
R[i] = stack.isEmpty() ? 0 : stack.peek() + 1;
stack.push(i);
}
// 计算 MAX(i) 并找出最大值
int maxProduct = 0;
for (int i = 0; i < n; i++) {
maxProduct = Math.max(maxProduct, L[i] * R[i]);
}
return maxProduct;
}
public static void main(String[] args) {
// Add your test cases here
System.out.println(solution(5, new int[]{5, 4, 3, 4, 5}) == 8);
}
}
关键点
- 单调栈的维护:在计算
L(i)和R(i)时,栈中始终保持单调递减的顺序。 - 索引的处理:栈中存储的是数组元素的索引,而不是元素值本身。
- 边界条件:当栈为空时,表示没有找到符合条件的元素,此时
L(i)或R(i)为 0。
复杂度分析
- 时间复杂度:
O(N),每个元素最多入栈和出栈一次。 - 空间复杂度:
O(N),用于存储L和R数组以及栈。
做题心得
写代码前要理好思路:
只有比栈顶元素小的元素才能直接进栈,否则需要先将栈中比当前元素小的元素出栈,再将当前元素入栈。
这样就保证了栈中保留的都是比当前入栈元素大的值,并且从栈顶到栈底的元素值是单调递增的。