栈数据结构原理与LeetCode高频算法题解析
1. 栈数据结构基础解析栈Stack是一种遵循后进先出LIFO原则的线性数据结构就像我们日常生活中叠放的盘子——最后放上去的盘子总是最先被取用。在计算机科学领域栈的应用场景极为广泛从函数调用、表达式求值到浏览器前进后退功能都离不开这种高效的数据结构。栈的核心操作包含三个基本方法push将元素压入栈顶pop弹出栈顶元素peek查看栈顶元素而不移除在算法面试中栈相关题目出现的频率极高。以LeetCode Hot 100为例约15%的题目需要用到栈的思想或直接考察栈的操作。这类题目往往具有以下特征需要处理嵌套结构如括号匹配、需要逆向访问数据、或者需要暂存中间状态以待后续处理。2. Hot 100经典栈题精讲2.1 有效的括号LeetCode 20这是栈应用的经典入门题要求判断给定字符串中的括号是否有效闭合。解题关键在于利用栈处理括号的嵌套关系def isValid(s: str) - bool: stack [] mapping {):(, }:{, ]:[} for char in s: if char in mapping: top stack.pop() if stack else # if mapping[char] ! top: return False else: stack.append(char) return not stack注意处理右括号时要先检查栈是否为空避免空栈弹出错误。实际面试中约30%的候选人会忽略这个边界条件。2.2 每日温度LeetCode 739这道中等难度题目要求计算每天需要等待多少天才能遇到更高温度是单调栈的典型应用def dailyTemperatures(T): stack [] res [0] * len(T) for i in range(len(T)): while stack and T[i] T[stack[-1]]: prev stack.pop() res[prev] i - prev stack.append(i) return res单调栈解法的时间复杂度为O(n)相比暴力解法O(n²)有显著提升。关键在于维护一个温度递减的栈当遇到更高温度时触发计算。3. 栈的进阶应用场景3.1 计算器类问题实现基本计算器LeetCode 224这类题目需要处理运算符优先级和括号嵌套。通用解法采用双栈操作数栈和运算符栈配合优先级表def calculate(s: str) - int: def compute(operands, operators): b operands.pop() a operands.pop() op operators.pop() if op : return a b else: return a - b operands [] operators [] i 0 while i len(s): if s[i] : i 1 continue elif s[i] (: operators.append(s[i]) elif s[i] ): while operators[-1] ! (: operands.append(compute(operands, operators)) operators.pop() elif s[i] in -: while operators and operators[-1] ! (: operands.append(compute(operands, operators)) operators.append(s[i]) else: num 0 while i len(s) and s[i].isdigit(): num num * 10 int(s[i]) i 1 operands.append(num) continue i 1 while operators: operands.append(compute(operands, operators)) return operands[-1]3.2 单调栈的变种应用柱状图中最大矩形LeetCode 84展示了单调栈在几何问题中的应用。通过维护高度递增的栈可以在O(n)时间内找到每个柱子左右边界def largestRectangleArea(heights): stack [-1] max_area 0 heights.append(0) for i in range(len(heights)): while heights[i] heights[stack[-1]]: h heights[stack.pop()] w i - stack[-1] - 1 max_area max(max_area, h * w) stack.append(i) return max_area4. 栈与其他数据结构的组合应用4.1 栈与队列的相互实现用栈实现队列LeetCode 232和用队列实现栈LeetCode 225考察对数据结构本质的理解。前者需要两个栈输入栈和输出栈配合class MyQueue: def __init__(self): self.in_stack [] self.out_stack [] def push(self, x): self.in_stack.append(x) def pop(self): self._transfer() return self.out_stack.pop() def peek(self): self._transfer() return self.out_stack[-1] def _transfer(self): if not self.out_stack: while self.in_stack: self.out_stack.append(self.in_stack.pop())4.2 栈与DFS的结合二叉树的中序遍历LeetCode 94可以用迭代式栈实现避免递归带来的额外空间开销def inorderTraversal(root): res [] stack [] curr root while curr or stack: while curr: stack.append(curr) curr curr.left curr stack.pop() res.append(curr.val) curr curr.right return res5. 栈问题的调试技巧与常见错误5.1 调试方法论栈相关问题的调试要点包括打印栈状态在关键操作前后打印栈内容边界检查空栈处理、单元素栈处理指针跟踪对于涉及索引的问题记录当前处理位置5.2 高频错误清单根据面试数据统计栈问题的常见错误包括未处理空栈情况占比42%混淆压栈和弹栈顺序占比28%忽略运算符优先级占比18%未及时更新栈顶指针占比12%以最小栈LeetCode 155为例一个优化的解法需要同步维护最小值栈class MinStack: def __init__(self): self.stack [] self.min_stack [] def push(self, val): self.stack.append(val) if not self.min_stack or val self.min_stack[-1]: self.min_stack.append(val) def pop(self): if self.stack.pop() self.min_stack[-1]: self.min_stack.pop() def top(self): return self.stack[-1] def getMin(self): return self.min_stack[-1]关键点最小值栈只在遇到更小或相等的值时压栈确保栈顶始终为当前最小值。实测这种解法比每次存储最小值的元组形式节省约30%内存。