最大乘积问题题解 | 豆包MarsCode AI刷题

118 阅读5分钟

题目描述

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

  • 函数 ( L(i) ):满足以下条件的最大的 ( j ) 值:

    • ( j < i )
    • ( a[j] > a[i] )
    • 如果找不到这样的 ( j ),那么 ( L(i) = 0 );如果有多个满足条件的 ( j ),选择离 ( i ) 最近的那个。
  • 函数 ( R(i) ):满足以下条件的最小的 ( k ) 值:

    • ( k > i )
    • ( a[k] > a[i] )
    • 如果找不到这样的 ( k ),那么 ( R(i) = 0 );如果有多个满足条件的 ( k ),选择离 ( i ) 最近的那个。

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

示例

  • 输入:( n = 5 ),array = [5, 4, 3, 4, 5]
    • 输出:8
  • 输入:( n = 6 ),array = [2, 1, 4, 3, 6, 5]
    • 输出:15
  • 输入:( n = 7 ),array = [1, 2, 3, 4, 5, 6, 7]
    • 输出:0

解题思路

分析需求

  • 输入:两个参数,分别是数组的长度 ( n ) 和数组 array
  • 输出:一个整数,表示 ( MAX(i) ) 的最大值。

关键点

  1. 定义函数 ( L(i) ) 和 ( R(i) )
    • ( L(i) ) 表示在 ( i ) 左侧最近的一个比 ( a[i] ) 大的元素的索引。
    • ( R(i) ) 表示在 ( i ) 右侧最近的一个比 ( a[i] ) 大的元素的索引。
  2. 计算 ( L(i) ) 和 ( R(i) )
    • 使用单调栈来高效地计算 ( L(i) ) 和 ( R(i) )。
  3. 计算 ( MAX(i) )
    • ( MAX(i) = L(i) \times R(i) )。
  4. 找到最大值
    • 遍历所有 ( i ),找到 ( MAX(i) ) 的最大值。

算法步骤

  1. 初始化
    • 创建两个数组 LR,分别用于存储 ( L(i) ) 和 ( R(i) )。
    • 创建一个栈 stack,用于辅助计算 ( L(i) ) 和 ( R(i) )。
  2. 计算 ( L(i) )
    • 从左到右遍历数组,使用栈来存储索引。
    • 对于每个元素 ( a[i] ),弹出栈中所有比 ( a[i] ) 小的元素,直到栈为空或栈顶元素大于 ( a[i] )。
    • 如果栈不为空,栈顶元素即为 ( L(i) ),否则 ( L(i) = 0 )。
    • 将当前索引 ( i ) 压入栈中。
  3. 计算 ( R(i) )
    • 从右到左遍历数组,使用栈来存储索引。
    • 对于每个元素 ( a[i] ),弹出栈中所有比 ( a[i] ) 小的元素,直到栈为空或栈顶元素大于 ( a[i] )。
    • 如果栈不为空,栈顶元素即为 ( R(i) ),否则 ( R(i) = 0 )。
    • 将当前索引 ( i ) 压入栈中。
  4. 计算 ( MAX(i) )
    • 遍历数组,计算每个 ( i ) 的 ( MAX(i) )。
    • 更新最大值 maxProduct
  5. 返回结果
    • 返回 maxProduct

代码解析

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<>();

        // 计算 L(i)
        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; // 注意:这里需要 +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; // 注意:这里需要 +1,因为题目要求的是索引
            stack.push(i);
        }

        // 计算 MAX(i) 并找到最大值
        int maxProduct = 0;
        for (int i = 0; i < n; i++) {
            int product = L[i] * R[i];
            if (product > maxProduct) {
                maxProduct = product;
            }
        }

        return maxProduct;
    }

    public static void main(String[] args) {
        // 添加测试用例
        System.out.println(solution(5, new int[] { 5, 4, 3, 4, 5 }) == 8);
        System.out.println(solution(6, new int[] { 2, 1, 4, 3, 6, 5 }) == 15);
        System.out.println(solution(7, new int[] { 1, 2, 3, 4, 5, 6, 7 }) == 0);
    }
}
  • 初始化数组:创建两个数组 LR,分别用于存储 ( L(i) ) 和 ( R(i) )。
  • 计算 ( L(i) ):从左到右遍历数组,使用栈来存储索引,确保栈中元素从栈底到栈顶是递减的。
  • 计算 ( R(i) ):从右到左遍历数组,使用栈来存储索引,确保栈中元素从栈底到栈顶是递减的。
  • 计算 ( MAX(i) ):遍历数组,计算每个 ( i ) 的 ( MAX(i) ),并更新最大值 maxProduct

学习心得

1. 理解问题的本质

在解决这个问题时,首先要理解 ( L(i) ) 和 ( R(i) ) 的定义,即在 ( i ) 左侧和右侧最近的一个比 ( a[i] ) 大的元素的索引。通过明确这一点,我们可以更容易地设计算法来解决问题。

2. 单调栈的应用

单调栈是一种高效的算法技术,适用于解决一系列与顺序相关的最值问题。在这个问题中,我们使用单调栈来高效地计算 ( L(i) ) 和 ( R(i) ),避免了暴力搜索的时间复杂度。

3. 边界条件的处理

在计算 ( L(i) ) 和 ( R(i) ) 时,需要注意边界条件的处理。特别是当栈为空时,需要将 ( L(i) ) 或 ( R(i) ) 设为 0,以符合题目的要求。

4. 数据结构的选择

选择合适的数据结构可以简化问题的解决过程。在这个问题中,我们使用了栈来存储索引,确保栈中元素从栈底到栈顶是递减的。这种做法不仅简单明了,而且效率高。

5. 测试用例的重要性

编写测试用例可以帮助我们验证算法的正确性。在这个问题中,我们提供了几个典型的测试用例,确保我们的算法能够处理各种边界情况。通过这些测试用例,我们可以及时发现和修复潜在的问题。

总结

通过解决这个问题,我深刻体会到了算法设计的重要性。合理的算法设计不仅可以提高程序的效率,还可以使代码更加清晰和易于维护。同时,我也学会了如何通过单调栈来优化算法,这对于解决复杂问题非常有帮助。在未来的学习和工作中,我会继续努力提升自己的算法能力,以应对更多挑战。