跳转至

443_栈与队列面试题


一句话说明

栈是后进先出(LIFO)、队列是先进先出(FIFO),面试里括号匹配/单调栈用栈,BFS用队列,两个栈可以互相模拟。


核心知识点

白话理解

栈像一摞盘子——最后放的最先拿。队列像排队买饭——先排的先吃。面试中括号是否匹配、温度要等多少天才升高,都是典型栈/队列题。

Python 实现

# 栈:用 list 末尾模拟
stack = []
stack.append(x)    # 入栈
stack.pop()        # 出栈(取末尾)
stack[-1]          # 查栈顶

# 队列:用 collections.deque
from collections import deque
q = deque()
q.append(x)        # 入队(尾部)
q.popleft()        # 出队(头部),比 list.pop(0) 快 O(1)
q[0]               # 查队头

经典题目与解法

题目1:有效的括号(LeetCode 20,栈经典)

题意:给定只含括号的字符串,判断括号是否有效(匹配且顺序正确)。

def is_valid(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                       # 栈为空说明全部匹配

# 测试
print(is_valid("()[]{}"))  # True
print(is_valid("(]"))      # False
print(is_valid("([)]"))    # False
print(is_valid("{[]}"))    # True

时间复杂度:O(n)
空间复杂度:O(n) 最坏全是左括号


题目2:每日温度(LeetCode 739,单调栈)

题意:给出每天的温度,返回每天需要等几天才能等到更高温度(等不到填0)。

def daily_temperatures(temperatures):
    """
    单调递减栈:栈里存下标,栈内对应温度始终递减
    当遇到比栈顶更高的温度,栈顶"等待结束",计算等待天数

    比喻:栈里是"还在排队等暖和天气的人的位置"
    新来一个热天,所有比它冷的天都出队计算
    """
    n = len(temperatures)
    result = [0] * n                       # 初始化结果数组
    stack = []                             # 单调递减栈,存下标

    for i in range(n):
        # 当前温度比栈顶温度高,栈顶等到了更暖的天
        while stack and temperatures[i] > temperatures[stack[-1]]:
            idx = stack.pop()              # 出栈:等待结束的那天
            result[idx] = i - idx          # 等待天数 = 当前下标 - 出栈下标

        stack.append(i)                    # 当前天入栈,开始等待

    # 栈中剩余的都是没等到更暖天的,result已初始化为0

    return result

# 测试
temps = [73, 74, 75, 71, 69, 72, 76, 73]
print(daily_temperatures(temps))  # [1, 1, 4, 2, 1, 1, 0, 0]

时间复杂度:O(n) 每个元素最多入栈出栈各一次
空间复杂度:O(n)


题目3:用两个栈实现队列(LeetCode 232)

题意:只能使用两个栈的操作(push/pop),实现队列的 push 和 pop。

class MyQueue:
    """
    两个栈模拟队列
    in_stack:接收新入队的元素
    out_stack:提供出队的元素

    当 out_stack 为空时,把 in_stack 全部倒入 out_stack
    (倒一次后顺序就正确了)
    """
    def __init__(self):
        self.in_stack = []                 # 入队用的栈
        self.out_stack = []                # 出队用的栈

    def push(self, x: int) -> None:
        self.in_stack.append(x)            # 直接压入 in_stack

    def pop(self) -> int:
        self._move_if_needed()             # 如果 out_stack 空,先转移
        return self.out_stack.pop()        # 从 out_stack 弹出

    def peek(self) -> int:
        self._move_if_needed()
        return self.out_stack[-1]          # 查看 out_stack 栈顶

    def empty(self) -> bool:
        return not self.in_stack and not self.out_stack

    def _move_if_needed(self):
        """out_stack 空时把 in_stack 全部倒入"""
        if not self.out_stack:
            while self.in_stack:
                self.out_stack.append(self.in_stack.pop())

# 测试
q = MyQueue()
q.push(1)
q.push(2)
print(q.peek())   # 1(先进先出)
print(q.pop())    # 1
print(q.empty())  # False

时间复杂度:push O(1);pop 均摊 O(1)(每个元素最多转移一次)
空间复杂度:O(n)


面试技巧

  1. 单调栈的核心:维持栈的单调性,入栈前弹出不满足单调条件的元素
  2. 两栈模拟队列是经典:面试官喜欢问"均摊复杂度",pop 均摊 O(1)
  3. BFS 必用队列:提到层序遍历、最短路径,第一反应是 queue + BFS
  4. 栈处理"最近的状态":括号匹配、函数调用栈、撤销操作都是栈
  5. deque 比 list 快:Python 里 list.pop(0) 是 O(n),用 deque.popleft() 才是 O(1)

速查表

from collections import deque

# 栈(LIFO)
stack = []
stack.append(x)         # 入栈 O(1)
stack.pop()             # 出栈 O(1)
stack[-1]               # 查栈顶 O(1)

# 队列(FIFO)
q = deque()
q.append(x)             # 入队 O(1)
q.popleft()             # 出队 O(1)
q[0]                    # 查队头 O(1)

# 单调栈模板
stack = []
for i in range(n):
    while stack and condition(nums[stack[-1]], nums[i]):
        idx = stack.pop()
        # 处理出栈元素
    stack.append(i)

# BFS 模板(用队列)
from collections import deque
queue = deque([start])
visited = {start}
while queue:
    node = queue.popleft()
    for neighbor in graph[node]:
        if neighbor not in visited:
            visited.add(neighbor)
            queue.append(neighbor)
场景数据结构例题
括号匹配LeetCode 20
下一个更大值单调栈LeetCode 739
层序遍历队列LeetCode 102
两栈模拟队列双栈LeetCode 232
最小值查询辅助栈LeetCode 155