单调栈(Monotonic Stack)是一种特殊的栈结构,它在算法题解中经常用于处理"下一个更大/更小元素"这类问题。与传统栈的区别在于,单调栈在入栈时会保持栈内元素的单调性(递增或递减)。
我第一次接触单调栈是在解决LeetCode 496题"下一个更大元素 I"时。当时暴力解法的时间复杂度是O(n²),而使用单调栈可以优化到O(n)。这种性能提升让我意识到单调栈在特定场景下的强大威力。
单调栈的核心特性在于:
单调递增栈是指栈底到栈顶元素保持递增顺序。这种结构常用于寻找"下一个更小元素"的问题。
典型应用场景:
python复制def increasing_stack(nums):
stack = []
for num in nums:
while stack and stack[-1] > num:
# 弹出栈顶元素并处理
top = stack.pop()
process(top)
stack.append(num)
单调递减栈则是栈底到栈顶元素保持递减顺序,适用于寻找"下一个更大元素"的问题。
典型应用场景:
python复制def decreasing_stack(nums):
stack = []
for num in nums:
while stack and stack[-1] < num:
# 弹出栈顶元素并处理
top = stack.pop()
process(top)
stack.append(num)
这是单调栈最经典的入门题。给定两个数组nums1和nums2,其中nums1是nums2的子集,要求找出nums1中每个元素在nums2中对应位置的下一个更大元素。
解法步骤:
python复制def nextGreaterElement(nums1, nums2):
stack = []
mapping = {}
for num in nums2:
while stack and stack[-1] < num:
mapping[stack.pop()] = num
stack.append(num)
return [mapping.get(num, -1) for num in nums1]
给定每日温度列表,要求返回一个列表,表示需要等待多少天才能观测到更高温度。
这个问题可以转化为寻找每个元素的下一个更大元素的位置差。
python复制def dailyTemperatures(T):
stack = []
res = [0] * len(T)
for i, temp in enumerate(T):
while stack and T[stack[-1]] < temp:
prev = stack.pop()
res[prev] = i - prev
stack.append(i)
return res
在实际编码中,边界条件的处理往往是最容易出错的地方:
单调栈的时间复杂度通常是O(n),因为每个元素最多入栈和出栈各一次。这比暴力解法的O(n²)要高效得多。
虽然单调栈需要额外的栈空间,但空间复杂度仍然是O(n)。在某些问题中,可以通过复用输入数组来进一步优化空间。
对于循环数组问题(如LeetCode 503),可以通过将数组长度翻倍或使用取模运算来处理:
python复制def nextGreaterElements(nums):
n = len(nums)
res = [-1] * n
stack = []
for i in range(2 * n):
while stack and nums[stack[-1] % n] < nums[i % n]:
res[stack.pop() % n] = nums[i % n]
stack.append(i)
return res
单调栈也可以扩展到二维问题,如最大矩形(LeetCode 85)和最大正方形问题。这类问题通常需要将二维问题转化为一系列的一维问题来处理。
为了真正掌握单调栈,建议按以下顺序练习:
基础问题:
中等难度:
进阶挑战:
我在最初学习单调栈时,花了大约一周时间专门练习这类问题。建议每天解决2-3道相关题目,并总结其中的共性和差异。