单调栈是一个特殊的栈数据结构,可以在 的时间复杂度内解决一些与栈相关的问题。它的特殊之处在于,它的元素可以保持单调性。
单调递增栈
单调递增栈是指栈中的元素从栈底到栈顶呈单调递增的顺序。常见的应用如下:
下一个更大的元素
给定一个数组,找出数组中每个元素的下一个更大的元素。如果找不到,则输出 -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
总结
单调栈可以在 的时间复杂度内解决一些与栈相关的问题,常用单调递增栈和单调递减栈。需要注意栈中保存的是元素的下标,而不是元素本身。另外,使用单调栈的时候,需要考虑清楚每个元素入栈后会对栈中的元素产生什么影响,才能正确解决问题。