一、每日温度(Leetcode 739)

思路:
-
栈里存放的是**"还没等到升温的日子"**的索引;
-
每遇到一个新的温度:
-
检查是否比栈顶的温度高;
-
如果高了,说明升温来了,栈顶元素可以出栈,并计算等待天数;
-
-
一旦栈为空或当前温度不高于栈顶,就入栈。
python
class Solution:
def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
n = len(temperatures)
res = [0] * n # 初始化结果数组
stack = [] # 存放的是索引,栈中温度递减
for i in range(n):
# 如果当前温度高于栈顶(之前某一天)的温度
while stack and temperatures[i] > temperatures[stack[-1]]:
prev_index = stack.pop()
res[prev_index] = i - prev_index # 计算天数差
stack.append(i) # 当前索引入栈
return res
二、下一个更大元素 I(Leetcode 496)

思路:
-
用单调栈求出
nums2
中每个元素右边第一个更大的数。 -
用字典
greater_map
存储这个映射关系:{元素: 它右边第一个更大的元素}
。 -
最后,对
nums1
中的每个数,从字典中查找答案,不存在则返回-1
。
python
class Solution:
def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]:
stack = []
greater_map = {}
for num in nums2:
# 栈中元素递减,当前元素比栈顶大 -> 出栈并记录
while stack and num > stack[-1]:
prev = stack.pop()
greater_map[prev] = num
stack.append(num)
# 栈中剩下的元素右边没有更大值,默认是 -1
return [greater_map.get(x, -1) for x in nums1]
三、下一个更大元素 II (Leetcode 503)

思路:
我们可以将数组遍历两遍(通过 %
模拟循环),来处理尾部比头部大的情况:
-
用
i % n
实现数组循环; -
只在第一轮把索引入栈,防止重复处理;
-
第二轮仅用来帮助"解决前面的元素右边没人比它大的问题"。
python
class Solution:
def nextGreaterElements(self, nums: List[int]) -> List[int]:
n = len(nums)
res = [-1] * n # 初始化结果为 -1
stack = []
# 遍历两轮数组
for i in range(2 * n):
num = nums[i % n]
while stack and nums[stack[-1]] < num:
index = stack.pop()
res[index] = num
if i < n:
stack.append(i) # 只在第一轮入栈
return res
四、接雨水(Leetcode 42)

1.动态规划:
-
当前格子能接多少水」取决于它左边和右边的最大高度中的较小值。
-
预先记录每个位置左侧和右侧最大高度
-
再计算每个位置能接多少水
python
class Solution:
def trap(self, height: List[int]) -> int:
n = len(height)
if n == 0:
return 0 # 边界处理:空数组无法存水
# 初始化左右最大高度数组
left_max = [0] * n
right_max = [0] * n
# 计算每个位置左边(包括自己)最高的柱子高度
left_max[0] = height[0]
for i in range(1, n):
# 当前左最大高度 = 前一个位置的左最大高度 或 当前高度,取较大者
left_max[i] = max(left_max[i - 1], height[i])
# 计算每个位置右边(包括自己)最高的柱子高度
right_max[-1] = height[-1]
for i in range(n - 2, -1, -1):
# 当前右最大高度 = 后一个位置的右最大高度 或 当前高度,取较大者
right_max[i] = max(right_max[i + 1], height[i])
# 遍历每个位置,计算该位置可接的水量
res = 0
for i in range(n):
# 当前柱子最多能接的水 = 左右最大高度的较小值 - 当前高度
# 如果左右最大高度都大于当前高度,才有水;否则为0
res += min(left_max[i], right_max[i]) - height[i]
return res # 返回总的接水量
2.单调栈:
-
遇到比栈顶高的柱子,说明可以接水,弹出栈顶,计算面积。
-
左边界:栈顶弹出后的新栈顶
-
右边界:当前元素
-
宽度 =
右边 - 左边 - 1
,高度 =min(height[left], height[right]) - height[中间]
python
class Solution:
def trap(self, height: List[int]) -> int:
stack = [] # 单调递减栈,存的是柱子的索引
res = 0 # 累加接的水量
for i, h in enumerate(height): # 遍历每个位置的柱子高度,i 是当前柱子的索引
# 当前高度 h 比栈顶柱子高,说明可以形成"凹槽"接水
while stack and h > height[stack[-1]]:
bottom = stack.pop() # 凹槽的底部索引
if not stack:
break # 栈空了说明左边没墙,不能接水
left = stack[-1] # 左边高墙的索引
width = i - left - 1 # 宽度 = 当前右墙索引 i - 左墙索引 left - 1
bounded_height = min(height[left], h) - height[bottom] # 高度 = 两边较矮墙 - 底部高度
res += width * bounded_height # 当前"凹槽"能装的水
stack.append(i) # 当前柱子索引入栈,可能成为新的"左墙"或"底部"
return res
3.双指针:
-
左右指针从两端向中间移动。
-
维护两个变量:
left_max
和right_max
,分别表示左侧和右侧最高的墙。 -
某位置的接水量取决于
min(left_max, right_max) - height[i]
。
python
class Solution:
def trap(self, height: List[int]) -> int:
if not height:
return 0 # 空数组无法接水,直接返回0
left, right = 0, len(height) - 1 # 左右指针
left_max, right_max = 0, 0 # 记录左右两侧最大高度
res = 0 # 存储总共接到的水量
while left < right:
# 谁小就移动谁,决定接水能力的是较矮一方
if height[left] < height[right]:
if height[left] >= left_max:
left_max = height[left] # 更新左侧最大值
else:
res += left_max - height[left] # 当前柱子能接的水 = 左侧最大高度 - 当前高度
left += 1 # 左指针右移
else:
if height[right] >= right_max:
right_max = height[right] # 更新右侧最大值
else:
res += right_max - height[right] # 当前柱子能接的水 = 右侧最大高度 - 当前高度
right -= 1 # 右指针左移
return res
-
总是处理 较矮的一侧,因为它才是限制水位高度的关键。
-
不需要额外数组,只用两个指针与两个变量记录左右最大值,节省空间,时间复杂度
O(n)
,空间复杂度O(1)
。
五、柱状图中最大的矩形(Leetcode 84)

单调栈:
-
对每个柱子,找到左边第一个小于它的柱子
left[i]
-
找到右边第一个小于它的柱子
right[i]
-
宽度为
right[i] - left[i] - 1
python
class Solution:
def largestRectangleArea(self, heights: List[int]) -> int:
n = len(heights)
left = [0] * n # 记录每个柱子左边第一个比它矮的位置索引
right = [n] * n # 记录每个柱子右边第一个比它矮的位置索引
stack = []
# 从左到右遍历,计算 left[i]
for i in range(n):
# 栈中维护单调递增的柱子索引
while stack and heights[stack[-1]] >= heights[i]:
stack.pop()
# 如果栈为空,说明左边没有比当前柱子矮的了
left[i] = stack[-1] if stack else -1
stack.append(i)
# 清空栈用于接下来的右边界计算
stack.clear()
# 从右到左遍历,计算 right[i]
for i in range(n - 1, -1, -1):
# 栈中维护单调递增的柱子索引
while stack and heights[stack[-1]] >= heights[i]:
stack.pop()
# 如果栈为空,说明右边没有比当前柱子矮的了
right[i] = stack[-1] if stack else n
stack.append(i)
max_area = 0
for i in range(n):
# 当前柱子能延展的宽度 = right[i] - left[i] - 1
width = right[i] - left[i] - 1
# 面积 = 高度 × 宽度
max_area = max(max_area, width * heights[i])
return max_area