当前位置: 首页 > 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
http://www.xdnf.cn/news/349489.html

相关文章:

  • VBA经典应用69例应用8:利用VBA,完成自动运行任务的预设
  • xiaopiu原型设计工具笔记
  • Windows 环境变量完全指南:系统变量、用户变量与 PATH 详解
  • 在不同环境下部署和运行基于后量子密码的轻量级通信协议的详细指南
  • pm2如何执行脚本批量启动多个服务
  • 认识守卫-以及简单的示例和装饰器
  • Java网络编程:理解URI、URL和URN
  • python线上学习进度报告
  • Android13 权限管理机制整理
  • 308.旅行终点站
  • 正点原子IMX6U开发板移植Qt时出现乱码
  • 什么是死信队列?死信队列是如何导致的?
  • TLS 1.3:一把打不开旧锁的新钥匙,为何难成主流?
  • Blind SSRF with Shellshock exploitation过关
  • [人机交互]以用户为中心的交互设计
  • 基于译码器和锁存器的运行逻辑的简易算法
  • 算法解密:轮转数组问题全解析
  • 多源地震资料处理中的震源信号分离算法资料
  • Java内存分配
  • 【git】git fsmonitor
  • 第四章:基于langchain构造一个完整RAG系统
  • 移动端返回指定页面
  • 本地聊天机器人部署方案
  • 《运维那些事儿》专栏总目录(持续更新)
  • SQLite3介绍与常用语句汇总
  • 【日撸 Java 三百行】Day 5(Switch语句)
  • SOA 与微服务架构深度比较
  • 【C语言】(8)—指针2
  • chrome插件提取标签数据
  • Python cv2对象检测与跟踪:从基础到进阶实战