数据结构 / 03

栈与队列

栈和队列是两种最基础的线性数据结构,区别仅在于元素的进出顺序。它们是很多复杂题型(单调栈、BFS、滑动窗口)的底层构件。

栈(Stack)

LIFO 原理

栈遵循 后进先出(Last In, First Out) 原则:最后放入的元素最先被取出。

Python 用 list 实现栈

listappendpop 都是对末尾操作,时间复杂度均为 O(1)。

stack = []
stack.append(1)
stack.append(2)
stack.append(3)   # stack = [1, 2, 3],栈顶是 3
top = stack[-1]   # peek:查看栈顶,值为 3
val = stack.pop() # pop:弹出栈顶,val = 3
if not stack:     # is_empty:判空
    print("栈为空")

时间复杂度

操作 复杂度
push (append) O(1) 均摊
pop O(1)
peek (stack[-1]) O(1)
is_empty O(1)

队列(Queue)

FIFO 原理

队列遵循 先进先出(First In, First Out) 原则:最先放入的元素最先被取出。

Python 用 collections.deque 实现队列

from collections import deque
queue = deque()
queue.append(1)
queue.append(2)
queue.append(3)        # deque([1, 2, 3])
val = queue.popleft()  # val = 1, deque([2, 3])
front = queue[0]       # 查看队首:2

为什么不用 list 做队列

list.pop(0) 需要将后面所有元素前移一位,时间复杂度为 O(n);而 deque.popleft() 基于双向链表实现,时间复杂度为 O(1)

双端队列(Deque)

deque(double-ended queue)两端都可以高效地进出元素,是 Python 中最通用的队列实现。

常用方法

from collections import deque
d = deque()
d.append(1)            # 右端入队
d.appendleft(0)        # 左端入队
d.pop()                # 右端出队
d.popleft()            # 左端出队
d.extend([2, 3])       # 右端批量入队
d.extendleft([-1, -2]) # 左端批量入队(顺序会反转)
d.rotate(1)            # 所有元素右移 1 位

deque 两端操作均为 O(1),但随机访问 d[i] 为 O(n),不适合频繁按索引取值。

高频题型

括号匹配

遇到左括号入栈,遇到右括号弹出栈顶检查是否匹配。

有效括号(LC 20):

def isValid(s: str) -> bool:
    stack = []
    mapping = {')': '(', ']': '[', '}': '{'}
    for ch in s:
        if ch in mapping:
            if not stack or stack[-1] != mapping[ch]:
                return False
            stack.pop()
        else:
            stack.append(ch)
    return len(stack) == 0

遍历结束后栈必须为空,否则说明有多余的左括号。

单调栈

什么是单调栈

单调栈维护栈内元素的单调递增或递减,每次入栈前先弹出所有破坏单调性的元素。核心价值:O(n) 时间找到每个元素左/右第一个更大(或更小)的元素。

下一个更大元素模板

def next_greater_element(nums):
    n = len(nums)
    result = [-1] * n
    stack = []  # 存索引,栈底到栈顶对应值单调递减
    for i in range(n):
        while stack and nums[i] > nums[stack[-1]]:
            idx = stack.pop()
            result[idx] = nums[i]
        stack.append(i)
    return result

每日温度(LC 739)

def dailyTemperatures(temperatures: list[int]) -> list[int]:
    n = len(temperatures)
    answer = [0] * n
    stack = []  # 存索引,栈内温度单调递减
    for i in range(n):
        while stack and temperatures[i] > temperatures[stack[-1]]:
            prev = stack.pop()
            answer[prev] = i - prev
        stack.append(i)
    return answer

用栈实现队列 / 用队列实现栈

LC 232 用栈实现队列

两个栈:in_stack 入队,out_stack 出队。出队时若 out_stack 为空,把 in_stack 全部倒入,顺序翻转实现 FIFO。均摊 O(1)。

class MyQueue:
    def __init__(self):
        self.in_stack = []
        self.out_stack = []
    def push(self, x: int) -> None:
        self.in_stack.append(x)
    def pop(self) -> int:
        self._move()
        return self.out_stack.pop()
    def peek(self) -> int:
        self._move()
        return self.out_stack[-1]
    def empty(self) -> bool:
        return not self.in_stack and not self.out_stack
    def _move(self):
        if not self.out_stack:
            while self.in_stack:
                self.out_stack.append(self.in_stack.pop())

LC 225 用队列实现栈

每次 push 后将前面的元素依次出队再入队,保持队首始终是最后入队的元素。

from collections import deque
class MyStack:
    def __init__(self):
        self.queue = deque()
    def push(self, x: int) -> None:
        self.queue.append(x)
        for _ in range(len(self.queue) - 1):
            self.queue.append(self.queue.popleft())
    def pop(self) -> int:
        return self.queue.popleft()
    def top(self) -> int:
        return self.queue[0]
    def empty(self) -> bool:
        return len(self.queue) == 0

最小栈(LC 155)

支持 O(1) 获取最小值。辅助栈同步记录每个状态下的最小值。

class MinStack:
    def __init__(self):
        self.stack = []
        self.min_stack = []
    def push(self, val: int) -> None:
        self.stack.append(val)
        cur_min = min(val, self.min_stack[-1] if self.min_stack else val)
        self.min_stack.append(cur_min)
    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]

所有操作均为 O(1),空间换时间。

经典题目

简单

题目 链接
有效的括号 LC 20
用栈实现队列 LC 232
用队列实现栈 LC 225
最小栈 LC 155

中等

题目 链接
每日温度 LC 739
下一个更大元素 II LC 503
字符串解码 LC 394
简化路径 LC 71

困难

题目 链接
柱状图中最大的矩形 LC 84
滑动窗口最大值 LC 239

小结

  • list队列deque,这是 Python 的标准做法。
  • 括号匹配、表达式求值、DFS 路径回溯都依赖栈。
  • BFS 遍历、任务调度依赖队列。
  • 单调栈是面试高频考点,核心模板务必熟练:维护栈的单调性,在弹出时更新答案。
  • 最小栈、用栈实现队列等设计题考查对数据结构的灵活组合,理解”辅助结构”和”延迟转移”的思想即可。