单调栈的使用

39 阅读4分钟

题目解析

本题描述了一个数组操作问题,其中涉及两个函数 L(i) 和 R(i),分别表示给定数组中某个元素左边和右边第一个比它大的元素的位置。通过这些位置,我们进一步定义了 MAX(i)=L(i)×R(i),目标是求出整个数组中 MAX(i) 的最大值。

这个问题的难点在于如何高效地计算 L(i)和 R(i)。如果直接暴力搜索左边和右边的元素,时间复杂度会达到 O(N^2),对于较大的数组可能会超时。因此,我们需要使用更高效的数据结构和算法优化。


解题思路

一、定义问题

  1. L(i) 是满足 j<i 且 a[j]>a[i] 的最大 j。如果不存在这样的 j,则 L(i)=0。
  2. R(i) 是满足 k>i 且 a[k]>a[i] 的最小 k。如果不存在这样的 k,则 R(i)=0。
  3. 我们需要找到数组中所有 i 的 MAX(i)=L(i)×R(i)的最大值。

二、单调栈优化

为了高效计算 L(i) 和 R(i),可以使用单调栈的技巧。

  1. 单调栈的原理

    • 单调栈是一种特殊的栈,在栈中元素是按一定单调性(递增或递减)排列的。
    • 它可以帮助我们快速找到比当前元素大或小的最近元素。
    • 通过在遍历过程中维护一个栈,确保栈内元素的有序性,可以实现 O(N) 的时间复杂度。
  2. 计算 L(i)

    • 从左到右遍历数组,用一个单调递减栈维护当前索引。
    • 当栈顶的元素小于或等于当前元素时,将栈顶元素弹出。
    • 栈顶的元素即为 L(i) 的索引。如果栈为空,则 L(i)=0。
  3. 计算 R(i)

    • 从右到左遍历数组,类似于计算 L(i),但这次用的是单调递减栈。
    • 栈顶的元素即为 R(i) 的索引。如果栈为空,则 R(i)=0。

三、实现步骤

  1. 初始化 L(i) 和 R(i) 数组,所有值初始为 0。
  2. 遍历数组计算 L(i),使用一个单调栈记录索引。
  3. 再次遍历数组计算 R(i),同样使用单调栈记录索引。
  4. 遍历数组,根据 MAX(i)=L(i)×R(i)计算出最大值。

算法实现

伪代码如下:

  1. 初始化栈 s 和数组 L,R。

  2. 计算 L(i):

    • 从左到右遍历 array:

      • 当栈顶元素不大于当前元素时,弹栈。
      • 如果栈不为空,栈顶索引即为 L(i),否则 L(i)=0。
      • 将当前索引压栈。
  3. 计算 R(i):

    • 从右到左遍历 array:

      • 当栈顶元素不大于当前元素时,弹栈。
      • 如果栈不为空,栈顶索引即为 R(i),否则 R(i)=0。
      • 将当前索引压栈。
  4. 遍历数组,计算每个 MAX(i)=L(i)×R(i)的值,并记录最大值。


时间和空间复杂度分析

  1. 时间复杂度

    • 遍历数组计算 L(i)和 R(i)各需要 O(N) 时间,因为每个元素最多被压栈和弹栈一次。
    • 最终遍历数组计算最大值需要 O(N) 时间。
    • 总时间复杂度为 O(N)。
  2. 空间复杂度

    • 使用了两个额外的数组 L 和 R,以及一个栈,空间复杂度为 O(N)。

示例分析

示例1

输入:array=[5,4,3,4,5]

  • 计算 L=[0,1,2,2,4]
  • 计算 R=[0,4,4,5,0]
  • MAX(i)=[0,4,8,10,0]最大值为 8。

示例2

输入:array=[2,1,4,3,6,5]

  • 计算 L=[0,1,0,3,0,5]
  • 计算 R=[3,3,5,5,0,0]
  • MAX(i)=[0,3,15,15,0,0],最大值为 15。

示例3

输入:array=[1,2,3,4,5,6,7]a

  • 所有 L 和 R 都为 0,因此 MAX(i)=0。

总结

本题通过单调栈优化计算 L(i)和 R(i),从而高效地解决问题。关键在于理解单调栈的性质,以及如何在遍历数组的同时维护一个有序栈。通过这种方法,可以将原本的 O(N^2) 算法优化到 O(N),使其适合处理大规模数据。