加载中…
应用场景:括号匹配 · 表达式求值 · 单调栈 · DFS
交互式动画演示,可调整参数并单步执行。
全屏打开栈(Stack) 是一种后进先出(LIFO, Last In First Out) 的线性数据结构。就像一叠盘子——最后放上去的盘子最先被拿走。
graph TD A[push A] --> Stack[(栈)] B[push B] --> Stack C[push C] --> Stack Stack -->|pop C| D[返回 C] Stack -->|pop B| E[返回 B] Stack -->|pop A| F[返回 A]
| 操作 | 说明 | 复杂度 |
|---|---|---|
push | 入栈(栈顶添加) | O(1) |
pop | 出栈(栈顶移除) | O(1) |
peek / top | 查看栈顶元素 | O(1) |
isEmpty | 判断是否为空 | O(1) |
size | 元素个数 | O(1) |
public class ArrayStack<E> {
private E[] data;
private int top = -1;
@SuppressWarnings("unchecked")
public ArrayStack(int capacity) {
data = (E[]) new Object[capacity];
}
public void push(E e) {
if (top == data.length - 1) throw new IllegalStateException("栈满");
data[++top] = e;
}
public E pop() {
if (top == -1) throw new NoSuchElementException("栈空");
E e = data[top];
data[top--] = null;
return e;
}
public E peek() {
if (top == -1) throw new NoSuchElementException("栈空");
return data[top];
}
public boolean isEmpty() { return top == -1; }
}public class LinkedStack<E> {
private Node<E> top;
private int size = 0;
private static class Node<E> { E val; Node<E> next; Node(E v) { val = v; } }
public void push(E e) {
Node<E> node = new Node<>(e);
node.next = top;
top = node;
size++;
}
public E pop() {
if (top == null) throw new NoSuchElementException();
E v = top.val;
top = top.next;
size--;
return v;
}
}| 实现 | 优点 | 缺点 |
|---|---|---|
| 顺序栈 | 缓存友好、访问快 | 容量固定 / 扩容有成本 |
| 链式栈 | 容量无限 | 节点开销、缓存不友好 |
Java 的
java.util.Stack是基于Vector实现的(线程安全但慢),生产推荐用ArrayDeque或Deque接口。
每调用一个函数,分配一个栈帧;返回时弹栈。这是编程语言实现的基础。
中缀: 3 + 4 * 2 - 5
后缀(逆波兰): 3 4 2 * + 5 -
算法:用栈存操作数,遇运算符弹出两个数计算,结果入栈。
public int evalRPN(String[] tokens) {
Deque<Integer> stack = new ArrayDeque<>();
for (String t : tokens) {
if ("+-*/".contains(t) && t.length() == 1) {
int b = stack.pop(), a = stack.pop();
switch (t) {
case "+": stack.push(a + b); break;
case "-": stack.push(a - b); break;
case "*": stack.push(a * b); break;
case "/": stack.push(a / b); break;
}
} else {
stack.push(Integer.parseInt(t));
}
}
return stack.pop();
}public boolean isValid(String s) {
Deque<Character> stack = new ArrayDeque<>();
Map<Character, Character> pairs = Map.of(')', '(', ']', '[', '}', '{');
for (char c : s.toCharArray()) {
if (pairs.containsValue(c)) stack.push(c);
else {
if (stack.isEmpty() || stack.pop() != pairs.get(c)) return false;
}
}
return stack.isEmpty();
}变种:
*)。见单独的"单调栈"专题教程。核心套路:维护栈的单调性,破坏时弹栈处理。
class TextEditor {
Deque<String> undo = new ArrayDeque<>();
Deque<String> redo = new ArrayDeque<>();
String text = "";
void edit(String change) {
undo.push(text);
text = change;
redo.clear();
}
void undo() {
if (undo.isEmpty()) return;
redo.push(text);
text = undo.pop();
}
}递归本身就是用栈实现的。手动"模拟递归"通常需要显式栈:
// 递归版本
void dfs(Node u) {
if (u == null) return;
visit(u);
dfs(u.left);
dfs(u.right);
}
// 显式栈版本(避免栈溢出)
void dfsIterative(Node root) {
if (root == null) return;
Deque<Node> stack = new ArrayDeque<>();
stack.push(root);
while (!stack.isEmpty()) {
Node u = stack.pop();
visit(u);
if (u.right != null) stack.push(u.right);
if (u.left != null) stack.push(u.left);
}
}栈在 DFS 中的作用:
1. 起点入栈。
2. 栈顶出栈 → 处理 → 子节点入栈。
3. 直到栈空。
注意:递归 DFS 就是调用栈实现的,二者本质相同。
要求 getMin() 也 O(1)。
思路:辅助栈同步记录"当前位置之上的最小值"。
class MinStack {
Deque<int[]> stack = new ArrayDeque<>();
public void push(int x) {
int min = stack.isEmpty() ? x : Math.min(stack.peek()[1], x);
stack.push(new int[]{x, min});
}
public void pop() { stack.pop(); }
public int top() { return stack.peek()[0]; }
public int getMin() { return stack.peek()[1]; }
}经典面试题:只许用栈实现队列的
push/pop/peek/empty。
class MyQueue {
Deque<Integer> in = new ArrayDeque<>();
Deque<Integer> out = new ArrayDeque<>();
public void push(int x) { in.push(x); }
public int pop() {
if (out.isEmpty()) while (!in.isEmpty()) out.push(in.pop());
return out.pop();
}
public int peek() {
if (out.isEmpty()) while (!in.isEmpty()) out.push(in.pop());
return out.peek();
}
public boolean empty() { return in.isEmpty() && out.isEmpty(); }
}摊销复杂度:每次元素最多"in→out"搬运一次 → O(1) 摊销。
| 操作 | 时间 | 空间 |
|---|---|---|
| push / pop / peek | O(1) | O(1) |
| 顺序栈 | O(1) | O(n) |
| 链式栈 | O(1) | O(n) |
Stack 类:已被标记 legacy,推荐用 ArrayDeque。| 难度 | 题目 | 类型 |
|---|---|---|
| 🟢 | 有效括号 | 括号匹配 |
| 🟢 | 用栈实现队列 | 双栈 |
| 🟡 | 最小栈 | 辅助栈 |
| 🟡 | 逆波兰表达式求值 | 表达式 |
| 🟡 | 比较含退格的字符串 | 双栈 |
| 🟡 | 字符串解码 | 嵌套解码 |
| 🟠 | 每日温度 | 单调栈 |
| 🟠 | 接雨水 | 单调栈 |
| 🟠 | 柱状图最大矩形 | 单调栈 |
| 🟠 | 移掉 K 位数字 | 单调栈 |
| 🔴 | 找出最长有效括号 | 栈/DP |
栈的本质:让"后到的先处理"。
看到"嵌套 / 匹配 / 后进先出 / 撤销"这些关键词,99% 是栈题。