当前位置: 首页 > news >正文

代码随想录第39天:单调栈

一、每日温度(Leetcode 739)

思路:

  • 栈里存放的是**“还没等到升温的日子”**的索引;

  • 每遇到一个新的温度:

    • 检查是否比栈顶的温度高;

    • 如果高了,说明升温来了,栈顶元素可以出栈,并计算等待天数;

  • 一旦栈为空或当前温度不高于栈顶,就入栈。

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)

思路:

  1. 用单调栈求出 nums2 中每个元素右边第一个更大的数。

  2. 用字典 greater_map 存储这个映射关系:{元素: 它右边第一个更大的元素}

  3. 最后,对 nums1 中的每个数,从字典中查找答案,不存在则返回 -1

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] = numstack.append(num)# 栈中剩下的元素右边没有更大值,默认是 -1return [greater_map.get(x, -1) for x in nums1]

三、下一个更大元素 II (Leetcode 503)

思路:

我们可以将数组遍历两遍(通过 % 模拟循环),来处理尾部比头部大的情况:

  • i % n 实现数组循环;

  • 只在第一轮把索引入栈,防止重复处理;

  • 第二轮仅用来帮助“解决前面的元素右边没人比它大的问题”。

class Solution:def nextGreaterElements(self, nums: List[int]) -> List[int]:n = len(nums)res = [-1] * n  # 初始化结果为 -1stack = []# 遍历两轮数组for i in range(2 * n):num = nums[i % n]while stack and nums[stack[-1]] < num:index = stack.pop()res[index] = numif i < n:stack.append(i)  # 只在第一轮入栈return res

四、接雨水(Leetcode 42)

1.动态规划:

  • 当前格子能接多少水」取决于它左边和右边的最大高度中的较小值。

  • 预先记录每个位置左侧和右侧最大高度

  • 再计算每个位置能接多少水

class Solution:def trap(self, height: List[int]) -> int:n = len(height)if n == 0:return 0  # 边界处理:空数组无法存水# 初始化左右最大高度数组left_max = [0] * nright_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 = 0for i in range(n):# 当前柱子最多能接的水 = 左右最大高度的较小值 - 当前高度# 如果左右最大高度都大于当前高度,才有水;否则为0res += min(left_max[i], right_max[i]) - height[i]return res  # 返回总的接水量

2.单调栈:

  • 遇到比栈顶高的柱子,说明可以接水,弹出栈顶,计算面积。

  • 左边界:栈顶弹出后的新栈顶

  • 右边界:当前元素

  • 宽度 = 右边 - 左边 - 1,高度 = min(height[left], height[right]) - height[中间]

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 - 1bounded_height = min(height[left], h) - height[bottom]  # 高度 = 两边较矮墙 - 底部高度res += width * bounded_height  # 当前“凹槽”能装的水stack.append(i)  # 当前柱子索引入栈,可能成为新的“左墙”或“底部”return res

3.双指针:

  • 左右指针从两端向中间移动。

  • 维护两个变量:left_maxright_max,分别表示左侧和右侧最高的墙。

  • 某位置的接水量取决于 min(left_max, right_max) - height[i]

class Solution:def trap(self, height: List[int]) -> int:if not height:return 0  # 空数组无法接水,直接返回0left, 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

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 -1stack.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 nstack.append(i)max_area = 0for i in range(n):# 当前柱子能延展的宽度 = right[i] - left[i] - 1width = right[i] - left[i] - 1# 面积 = 高度 × 宽度max_area = max(max_area, width * heights[i])return max_area

相关文章:

  • 零拷贝的简单复习
  • Python核心数据结构深度对比:列表、字典、元组与集合的异同与应用场景
  • 深度解析语义分割评估指标:从基础到创新实践
  • DataWorks快速入门
  • 预渲染 Prerender
  • pm2如何执行脚本批量启动多个服务
  • 77.组合问题
  • C++ STL入门:vecto容器
  • nextjs站点地图sitemap添加
  • 算法题(142):木材加工
  • Music AI Sandbox:打开你的创作新世界
  • java使用MinIO,虚拟机时间异常
  • Typora安装使用教程,图片相对储存路径路径设置
  • 滚珠导轨:电子制造领域精密运动的核心支撑
  • 跨线程和跨进程通信还有多种方式对比
  • 大模型(LLMs)agent
  • C++之“继承”
  • 年龄估计数据集
  • 深入探索 JavaScript 中的模块对象
  • 面试问题(连载。。。。)
  • 深圳两家会所涉卖淫嫖娼各被罚7万元逾期未缴,警方发催告书
  • 教育部答澎湃:2025世界数字教育大会将发布系列重磅成果
  • A股三大股指低收:银行股再度走强,两市成交11920亿元
  • 公募基金行业迎系统性变革:基金公司业绩差必须少收费
  • 8小时《大师与玛格丽特》:长度可以是特点,但不是价值标准
  • 金融监管总局:支持银行有序设立科技金融专门机构,推动研发机器人、低空飞行器等新兴领域的保险产品