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

106 阅读4分钟

代码段的目的是计算一组整数中,每个元素左右的相邻元素中较大的那个元素的最大乘积。具体来说,代码使用两个一维数组 ( L ) 和 ( R ) 来存储与每个元素相关的值,从而计算出对于每个元素的值 ( MAX(i) = L(i) * R(i) )。下面是对代码的逐步解析:

代码解释:

  1. 初始化数组 LR:

    int[] L = new int[n];
    int[] R = new int[n];
    for (int i = 0; i < n; i++) {
        L[i] = 0;
        R[i] = 0;
    }
    

    这里创建了长度为 ( n ) 的数组 LR,并将它们的所有值初始化为 0。这两个数组用于存储每个元素的左边和右边的值的索引。

  2. 计算 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);
    }
    
    • 使用单调栈的方式从左到右遍历数组。
    • 当前元素 array[i] 的左边元素(即 L(i))被设置为最近的比 array[i] 大的元素的索引。
    • 如果栈为空,说明 array[i] 左边没有比它大的元素,则 L[i] 为 0。
    • 最终 L 数组对于每个元素 i 存储了一个有效的索引,指向它左边第一个大于 array[i] 的元素。
  3. 计算 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);
    }
    
    • 这部分的逻辑与计算 L(i) 部分类似,只不过是从右到左遍历数组,计算每个元素右边的最近较大的元素。
    • 使用清空栈的方法,确保在这次计算中使用一个新的栈。
  4. 计算最大乘积:

    int maxProduct = 0;
    for (int i = 0; i < n; i++) {
        maxProduct = Math.max(maxProduct, L[i] * R[i]);
    }
    return maxProduct;
    
    • 最后,迭代数组,计算每个元素的乘积 ( L(i) * R(i) ),同时更新当前最大的乘积。

总结:

该算法有效使用了单调栈来减少复杂度,从而使得在计算每个元素左右第一个较大元素时避免了重复遍历,可以在 ( O(n) ) 时间复杂度内完成计算。这对于大数组的情况特别高效。

这段代码的目的是计算一组整数中,每个元素左右的相邻元素中较大的那个元素的最大乘积。具体来说,代码通过使用单调栈和两个辅助数组 ( L ) 和 ( R ) 来存储每个元素左右最近的较大元素的索引,从而计算出最大乘积。下面是对代码的详细原理解析。

原理解析

  1. 初始化数组 LR

    • L[i] 用于存储数组中第 i 个元素左边第一个大于 array[i] 的元素的索引(如果不存在则为 0)。
    • R[i] 用于存储数组中第 i 个元素右边第一个大于 array[i] 的元素的索引(同样,如果不存在则为 0)。
    • 初始化这两个数组为 0,表示尚未找到任何较大的元素。
  2. 计算 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);
    }
    
    • 使用单调栈从左到右遍历数组。
    • 对于每个元素 array[i],将栈中所有小于等于 array[i] 的索引弹出,保持栈中的元素是递减的。
    • 如果栈为空,表示没有找到比 array[i] 大的元素,L[i] 设置为 0。否则,L[i] 设置为栈顶元素的索引加 1(因为我们需要的是索引)。
    • 最后,将当前元素的索引 i 压入栈中,准备处理后续元素。
  3. 计算 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);
    }
    
    • 这部分逻辑与计算 L(i) 类似,但从右到左遍历数组,计算每个元素右边的最近较大的元素。
    • 清空栈以确保可以重新使用,避免干扰。
  4. 计算最大乘积

    int maxProduct = 0;
    for (int i = 0; i < n; i++) {
        maxProduct = Math.max(maxProduct, L[i] * R[i]);
    }
    return maxProduct;
    
    • 遍历 LR 数组,计算每个元素的乘积 ( L(i) \times R(i) )。
    • 更新 maxProduct 为当前计算得到的最大值。

整体复杂度

  • 时间复杂度:O(n),因为每个元素最多被压入和弹出栈一次。
  • 空间复杂度:O(n),用于存储 LR 数组以及栈。

总结

该算法通过利用单调栈的特性,避免了重复遍历数组的必要性,从而高效地计算每个元素左右第一个较大元素的索引,并最终得出最大乘积。这种方法特别适合处理大规模数据,能够在保持线性时间复杂度的同时有效解决问题。