单调栈

93 阅读3分钟

单调栈是一个特殊的栈数据结构,可以在 O(n)O(n) 的时间复杂度内解决一些与栈相关的问题。它的特殊之处在于,它的元素可以保持单调性。

单调递增栈

单调递增栈是指栈中的元素从栈底到栈顶呈单调递增的顺序。常见的应用如下:

下一个更大的元素

给定一个数组,找出数组中每个元素的下一个更大的元素。如果找不到,则输出 -1。

例如,给定数组 [2, 5, 3, 7, 1, 8],输出 [5, 7, 7, 8, 8, -1]。

使用单调递增栈,遍历数组,并将元素下标入栈。当遍历到一个新的元素时,如果它比栈顶元素小,则将其加入栈中;否则,将栈顶元素出栈,并将其下一个更大的元素设为当前元素。最后,将所有无法找到下一个更大元素的元素设为 -1。

def next_greater_element(nums): 
   n = len(nums) 
   res = [-1] * n 
   stack = [] 
   for i in range(n): 
      while stack and nums[i] > nums[stack[-1]]: 
        j = stack.pop() 
   res[j] = nums[i] 
   stack.append(i) 
   return res 

接雨水

给定一个数组,代表一个柱状图,数组中每个元素代表柱子的高度。计算这个柱状图能接多少雨水。

例如,给定数组 [0,1,0,2,1,0,1,3,2,1,2,1],输出 6。

使用单调递增栈,遍历数组,并将元素下标入栈。当遍历到一个新的元素时,如果它比栈顶元素小,则将其加入栈中;否则,将栈顶元素出栈,并计算接雨水的面积。

最后,将所有元素出栈,同时计算接雨水的面积。

def trap(height):
    n = len(height)
    res = 0
    stack = []
    for i in range(n):
        while stack and height[i] > height[stack[-1]]:
            j = stack.pop()
            if stack:
                k = stack[-1]
                res += (min(height[i], height[k]) - height[j]) * (i - k - 1)
        stack.append(i)
    return res

单调递减栈

单调递减栈是指栈中的元素从栈底到栈顶呈单调递减的顺序。常见的应用如下:

下一个更小的元素

给定一个数组,找出数组中每个元素的下一个更小的元素。如果找不到,则输出 -1。

例如,给定数组 [2, 5, 3, 7, 1, 8],输出 [1, 3, 1, 1, -1, -1]。

使用单调递减栈,遍历数组,并将元素下标入栈。当遍历到一个新的元素时,如果它比栈顶元素大,则将栈顶元素出栈,并将其下一个更小的元素设为当前元素。最后,将所有无法找到下一个更小元素的元素设为 -1。

def next_smaller_element(nums):
    n = len(nums)
    res = [-1] * n
    stack = []
    for i in range(n):
        while stack and nums[i] < nums[stack[-1]]:
            j = stack.pop()
            res[j] = nums[i]
        stack.append(i)
    return res

柱状图中最大的矩形

给定一个数组,代表一个柱状图,数组中每个元素代表柱子的高度。找出这个柱状图中面积最大的矩形。

例如,给定数组 [2, 1, 5, 6, 2, 3],输出 10。

使用单调递增栈,遍历数组,并将元素下标入栈。当遍历到一个新的元素时,如果它比栈顶元素小,则将栈顶元素出栈,并计算以它为高的矩形的面积。如果新元素比栈顶元素大,则将其加入栈中。最后,将所有元素出栈,同时计算栈顶元素为高的面积。

def largest_rectangle_area(heights):
    n = len(heights)
    res = 0
    stack = [-1]
    for i in range(n):
        while stack[-1] != -1 and heights[i] <= heights[stack[-1]]:
            j = stack.pop()
            res = max(res, heights[j] * (i - stack[-1] - 1))
        stack.append(i)
    while stack[-1] != -1:
        j = stack.pop()
        res = max(res, heights[j] * (n - stack[-1] - 1))
    return res

总结

单调栈可以在 O(n)O(n) 的时间复杂度内解决一些与栈相关的问题,常用单调递增栈和单调递减栈。需要注意栈中保存的是元素的下标,而不是元素本身。另外,使用单调栈的时候,需要考虑清楚每个元素入栈后会对栈中的元素产生什么影响,才能正确解决问题。