问题描述
小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) 和 R(i) 的定义:
- L(i) 是左边第一个比 a[i] 大的元素的索引。
- R(i) 是右边第一个比 a[i] 大的元素的索引。
-
数据结构选择:
- 使用栈来辅助计算 L(i) 和 R(i)。栈可以帮助我们高效地找到左边或右边第一个比当前元素大的元素。
-
算法步骤:
-
计算 L(i):
- 从左到右遍历数组,使用栈来记录当前元素左边第一个比它大的元素的索引。
- 如果栈顶元素比当前元素小,则弹出栈顶元素,直到栈为空或栈顶元素比当前元素大。
- 如果栈不为空,则栈顶元素就是 L(i) 的值;否则,L(i)=0。
-
计算 R(i):
- 从右到左遍历数组,使用栈来记录当前元素右边第一个比它大的元素的索引。
- 如果栈顶元素比当前元素小,则弹出栈顶元素,直到栈为空或栈顶元素比当前元素大。
- 如果栈不为空,则栈顶元素就是 R(i) 的值;否则,R(i)=0。
-
计算 MAX(i):
- 遍历数组,计算 MAX(i)=L(i)∗R(i),并找到其中的最大值。
-
import java.util.Stack;
public class Main {
public static int solution(int n, int[] array) {
int[] L = new int[n];
int[] R = new int[n];
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);
}
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);
}
int maxVal = 0;
for (int i = 0; i < n; i++) {
int maxProduct = L[i] * R[i];
if (maxProduct > maxVal) {
maxVal = maxProduct;
}
}
return maxVal;
}
public static void main(String[] args) {
System.out.println(solution(5, new int[]{5, 4, 3, 4, 5}) == 8);
}
}
知识点总结
-
栈的应用:
- 单调栈:栈中元素保持单调性(递增或递减),用于解决“下一个更大元素”或“上一个更大元素”的问题。
- 在本题中,我们使用单调栈来找到每个元素左边和右边第一个比它大的元素的索引。
-
数组操作:
- 数组的遍历:从左到右和从右到左遍历数组,分别计算每个元素的
L(i)和R(i)。 - 数组的索引操作:通过索引操作来访问和修改数组中的元素。
- 数组的遍历:从左到右和从右到左遍历数组,分别计算每个元素的
-
时间复杂度分析:
- 线性时间复杂度:通过使用栈,我们可以在 O(n) 的时间复杂度内计算出
L(i)和R(i),其中 n 是数组的长度。 - 空间复杂度:需要额外的空间来存储
L(i)和R(i)数组,以及栈的空间,总体空间复杂度为 O(n)。
- 线性时间复杂度:通过使用栈,我们可以在 O(n) 的时间复杂度内计算出
-
算法设计:
- 分治法:将问题分解为两个子问题(计算
L(i)和R(i)),然后合并结果(计算MAX(i))。 - 动态规划:虽然本题没有显式使用动态规划,但通过记录中间结果(
L(i)和R(i)),我们可以避免重复计算。
- 分治法:将问题分解为两个子问题(计算
通过使用单调栈和数组操作,我们可以在 O(n) 的时间复杂度内解决这个问题。关键在于理解如何使用栈来找到每个元素左边和右边第一个比它大的元素的索引,并计算 MAX(i) 的最大值。