news 2026/11/4 12:54:54

栈结构在算法中的应用与LeetCode经典题解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
栈结构在算法中的应用与LeetCode经典题解

1. 栈结构在算法中的核心价值

栈(Stack)作为计算机科学中最基础的数据结构之一,其"后进先出"(LIFO)的特性在算法设计中具有不可替代的作用。在实际工程和算法面试中,栈相关题目出现频率极高,LeetCode热题100中栈相关题目就占据了重要位置。掌握栈的精髓不仅能帮助我们高效解决特定类型问题,更能培养我们抽象问题本质的思维能力。

我刷题过程中发现,栈结构特别适合处理具有"最近相关性"的问题。比如括号匹配需要检查最近的开放括号,温度升高需要回溯最近的低温日期,这些场景下栈的LIFO特性都能完美匹配问题需求。接下来我将通过5道经典题目,拆解栈的不同应用范式。

2. 有效的括号(LeetCode 20)

2.1 问题本质与栈的适配性

给定一个只包含 '(', ')', '{', '}', '[' 和 ']' 的字符串,判断字符串是否有效。有效需满足:

  1. 左括号必须用相同类型的右括号闭合
  2. 左括号必须以正确的顺序闭合

这个问题的核心在于"最近打开的括号必须最先闭合",这与栈的LIFO特性完全吻合。我们可以遍历字符串,遇到左括号就压栈,遇到右括号就检查栈顶是否匹配。

2.2 实现细节与边界处理

def isValid(s: str) -> bool: stack = [] mapping = {')': '(', '}': '{', ']': '['} for char in s: if char in mapping: # 遇到右括号 top_element = stack.pop() if stack else '#' if mapping[char] != top_element: return False else: # 遇到左括号 stack.append(char) return not stack

关键点:

  1. 使用字典存储括号映射关系,代码更简洁
  2. 遇到右括号时需要检查栈是否为空(比如输入为"]"的情况)
  3. 最终需要检查栈是否为空(比如输入为"["的情况)

实际编码时最容易忽略的是右括号出现在首位的情况,这时直接pop会导致异常。我的经验是给pop操作添加默认值'#'作为保护。

3. 最小栈(LeetCode 155)

3.1 设计要求与实现方案

设计一个支持 push、pop、top 操作,并能在常数时间内检索到最小元素的栈。

最直接的思路是使用辅助栈同步存储最小值:

  • 主栈正常压入元素
  • 辅助栈压入当前最小值(新元素与辅助栈顶的较小者)
class MinStack: def __init__(self): self.stack = [] self.min_stack = [] def push(self, val: int) -> None: self.stack.append(val) if not self.min_stack or val <= self.min_stack[-1]: self.min_stack.append(val) else: self.min_stack.append(self.min_stack[-1]) def pop(self) -> None: self.stack.pop() self.min_stack.pop() def top(self) -> int: return self.stack[-1] def getMin(self) -> int: return self.min_stack[-1]

3.2 空间优化方案

辅助栈可以只存储最小值变化时的记录:

class MinStack: def __init__(self): self.stack = [] self.min_stack = [] def push(self, val: int) -> None: self.stack.append(val) if not self.min_stack or val <= self.min_stack[-1][0]: self.min_stack.append((val, len(self.stack))) def pop(self) -> None: if self.stack: if len(self.stack) == self.min_stack[-1][1]: self.min_stack.pop() self.stack.pop()

这种方案在最小值频繁重复时能显著节省空间,但pop逻辑更复杂。实际工程中需要根据数据特点权衡选择。

4. 字符串解码(LeetCode 394)

4.1 嵌套结构的处理思路

给定一个编码字符串如 "3[a2[c]]",返回解码后的字符串 "accaccacc"。

这类嵌套结构的问题,栈是最自然的解决方案。我们需要处理三种情况:

  1. 遇到数字:开始累积数字(注意多位数情况)
  2. 遇到字母:累积到当前字符串
  3. 遇到'[':将当前字符串和数字压栈,重置状态
  4. 遇到']':弹出栈顶的数字和字符串,进行拼接

4.2 完整实现与调试技巧

def decodeString(s: str) -> str: stack = [] current_str = "" current_num = 0 for char in s: if char.isdigit(): current_num = current_num * 10 + int(char) elif char == '[': stack.append((current_str, current_num)) current_str = "" current_num = 0 elif char == ']': prev_str, num = stack.pop() current_str = prev_str + current_str * num else: current_str += char return current_str

调试这类问题时,建议在纸上画出栈的变化过程。常见错误包括:

  1. 数字累积时未处理多位数(如"100[abc]")
  2. 遇到']'时拼接顺序错误(应该是前字符串+当前字符串×数字)
  3. 栈中存储的元素类型混乱(建议使用元组明确存储字符串和数字)

5. 每日温度(LeetCode 739)

5.1 单调栈的应用场景

给定每日温度列表,返回一个列表表示需要等待多少天才能遇到更暖和的温度。

这是典型的"下一个更大元素"问题,单调栈能高效解决。我们维护一个栈,存储尚未找到更高温度的日期索引。对于每一天的温度,我们检查栈顶元素对应的温度是否小于当前温度,如果是则计算天数差并弹出。

5.2 实现优化与复杂度分析

def dailyTemperatures(temperatures: List[int]) -> List[int]: stack = [] result = [0] * len(temperatures) for i, temp in enumerate(temperatures): while stack and temperatures[stack[-1]] < temp: prev_index = stack.pop() result[prev_index] = i - prev_index stack.append(i) return result

时间复杂度O(n):每个元素最多入栈出栈各一次 空间复杂度O(n):最坏情况下栈需要存储所有元素

实际编码时容易混淆栈中存储的是温度值还是索引。存储索引更实用,因为既可以通过列表访问温度值,又可以直接计算天数差。

6. 柱状图中最大的矩形(LeetCode 84)

6.1 问题转化与单调栈变种

给定n个非负整数表示柱状图的高度,求能勾勒出的最大矩形面积。

这个问题可以转化为:对于每个柱子,找到左右两边第一个比它矮的柱子。这正好是单调栈能解决的问题,不过这次我们需要维护一个单调递增的栈。

6.2 边界处理与代码实现

def largestRectangleArea(heights: List[int]) -> int: stack = [] max_area = 0 heights = [0] + heights + [0] # 添加哨兵 for i, h in enumerate(heights): while stack and heights[stack[-1]] > h: height = heights[stack.pop()] width = i - stack[-1] - 1 max_area = max(max_area, height * width) stack.append(i) return max_area

关键技巧:

  1. 在首尾添加高度为0的哨兵,简化边界条件处理
  2. 计算宽度时使用当前索引和新的栈顶索引
  3. 弹出元素时立即计算以该高度为高的最大矩形面积

复杂度分析:

  • 时间复杂度O(n):每个柱子最多入栈出栈各一次
  • 空间复杂度O(n):栈的空间消耗

7. 栈问题的解题模式总结

通过这五道题目,我们可以总结出栈在算法问题中的典型应用场景:

  1. 配对问题:如括号匹配,栈能高效检查最近相关性
  2. 递归模拟:如字符串解码,栈可以显式模拟递归调用栈
  3. 单调栈:处理"下一个更大/更小元素"类问题
  4. 历史记录:如最小栈,需要保存操作历史状态

实际解题时,我通常会先问自己:

  • 这个问题是否有"最近相关性"的特征?
  • 是否需要保存中间状态或历史记录?
  • 数据是否呈现某种单调性?

如果答案是肯定的,那么栈很可能就是合适的解决方案。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/11/4 12:53:21

AML8726MX芯片刷机工具:老电视盒线刷救砖与避坑指南

简介&#xff1a;面向晶晨AML系列芯片机顶盒与网络电视盒的USB线刷工具包&#xff0c;主要服务于需要为乐视C1s等AML8726MX设备进行固件恢复、系统升级或刷机排障的玩家与维修人员&#xff0c;也适用于系统无法启动、升级失败后的修复场景。压缩包共162个文件&#xff0c;整体约…

作者头像 李华
网站建设 2026/11/4 12:52:07

代码辅助工具安全配置与优化实践指南

1. 项目概述 最近在开发过程中&#xff0c;我发现很多开发者对于如何安全稳定地使用代码辅助工具存在不少困惑。作为一个长期使用各类编程辅助工具的开发者&#xff0c;我想分享一些实际经验&#xff0c;帮助大家避免常见的使用风险。 2. 核心需求解析 2.1 为什么需要安全配…

作者头像 李华
网站建设 2026/11/4 12:51:07

Agent指令解析、人格设定与技能路由的工程化配置实践

先说一个场景。前阵子帮一个团队做内部Agent平台的配置评审&#xff0c;会议室里坐了七八个人&#xff0c;运维、安全、平台、业务各来一位。项目负责人打开配置文件&#xff0c;第一页写着"人格&#xff1a;破甲模式&#xff1b;技能&#xff1a;全量放开&#xff1b;工作…

作者头像 李华
网站建设 2026/11/4 12:48:27

IMU预积分与旋转残差雅可比矩阵详解

1. IMU预积分技术背景解析在惯性导航与视觉SLAM融合的领域里&#xff0c;IMU预积分技术已经成为多传感器融合的黄金标准。我第一次接触这个概念是在2017年参与无人机定位项目时&#xff0c;当时为了处理高频IMU数据与低频视觉数据的时间对齐问题&#xff0c;团队决定采用预积分…

作者头像 李华
网站建设 2026/11/4 12:47:10

电科金仓数据库在中国中车轨道交通系统的应用实践

1. 电科金仓与中国中车数据库合作项目解析 最近在国产数据库领域有个值得关注的动态——电科金仓与中国中车达成了多批次数据库采购合作。作为长期关注国产基础软件发展的从业者&#xff0c;我觉得有必要深入分析一下这个合作背后的技术价值和行业意义。 中国中车作为全球轨道…

作者头像 李华
网站建设 2026/11/4 12:46:01

构建技术知识体系:从骨架搭建到细节精讲

1. 项目概述 "高屋建瓴的骨架、核心思想的深度剖析以及关键细节的精讲"这个标题让我想起了建筑工地上的场景——先搭钢筋骨架&#xff0c;再浇筑混凝土&#xff0c;最后精装修。这种从宏观到微观的思考方式&#xff0c;正是我在技术写作和知识分享领域深耕十余年所坚…

作者头像 李华