目录
[1. 栈(Stack)------ 后进先出(Last In First Out)](#1. 栈(Stack)—— 后进先出(Last In First Out))
[2. 队列(Queue)------ 先进先出(First In First Out)](#2. 队列(Queue)—— 先进先出(First In First Out))
[1. 问题场景](#1. 问题场景)
[2. 解题思路](#2. 解题思路)
[3. 代码实现(Java)](#3. 代码实现(Java))
[4. 代码关键解释](#4. 代码关键解释)
[1. 问题场景](#1. 问题场景)
[2. 解题思路](#2. 解题思路)
[3. 代码实现(Java)](#3. 代码实现(Java))
[4. 代码关键解释](#4. 代码关键解释)
栈与队列 ------从原理到实战
一、核心原理:栈(LIFO)与队列(FIFO)
1. 栈(Stack)------ 后进先出(Last In First Out)
- 通俗理解:像叠盘子,最后放上去的盘子,最先被拿走;只能从「栈顶」操作元素(入栈 / 出栈)。
- 核心操作 :
push():入栈(向栈顶添加元素)pop():出栈(从栈顶移除元素,返回该元素)peek():查看栈顶元素(不移除)isEmpty():判断栈是否为空
- Java 实现 :推荐使用
Deque(双端队列)代替过时的Stack类(Stack是遗留类,线程安全但性能差),Deque的push()/pop()方法可模拟栈。
2. 队列(Queue)------ 先进先出(First In First Out)
- 通俗理解:像排队买票,最先排队的人,最先买到票;只能从「队尾」入队,「队首」出队。
- 核心操作 :
offer():入队(向队尾添加元素,失败返回 false,比add()更安全)poll():出队(从队首移除元素,返回该元素,队列为空返回 null)peek():查看队首元素(不移除)isEmpty():判断队列是否为空
- Java 实现 :常用
LinkedList实现Queue接口(LinkedList同时实现了List和Deque)。
二、栈的典型应用:括号匹配
1. 问题场景
判断一个字符串中的括号是否合法匹配,例如:
- 合法:
()[]{},({[]}) - 非法:
(],([)],{}(少左括号),({}(少右括号)
2. 解题思路
- 遍历字符串的每个字符;
- 遇到左括号 (
(/[/{),直接入栈; - 遇到右括号 :
- 如果栈为空(无左括号匹配),直接返回
false; - 弹出栈顶元素,判断是否与当前右括号「成对」,不成对则返回
false;
- 如果栈为空(无左括号匹配),直接返回
- 遍历结束后,若栈不为空(有未匹配的左括号),返回
false;否则返回true。
3. 代码实现(Java)
java
import java.util.Deque;
import java.util.LinkedList;
/**
* 栈实现括号匹配
*/
public class BracketMatch {
public static boolean isValid(String s) {
// 1. 初始化双端队列作为栈(推荐用法)
Deque<Character> stack = new LinkedList<>();
// 2. 遍历字符串每个字符
for (char c : s.toCharArray()) {
// 3. 左括号入栈
if (c == '(' || c == '[' || c == '{') {
stack.push(c);
} else {
// 4. 右括号:先判断栈是否为空(无左括号匹配)
if (stack.isEmpty()) {
return false;
}
// 5. 弹出栈顶元素,判断是否匹配
char top = stack.pop();
if ((c == ')' && top != '(') ||
(c == ']' && top != '[') ||
(c == '}' && top != '{')) {
return false;
}
}
}
// 6. 遍历结束后,栈必须为空(无剩余左括号)
return stack.isEmpty();
}
// 测试用例
public static void main(String[] args) {
System.out.println(isValid("()[]{}")); // true
System.out.println(isValid("(]")); // false
System.out.println(isValid("({[]})")); // true
System.out.println(isValid("({})")); // true
System.out.println(isValid("{")); // false
}
}
4. 代码关键解释
Deque<Character> stack = new LinkedList<>():用 LinkedList 实现 Deque,模拟栈(比原生 Stack 更优);- 右括号处理逻辑:先判空再弹栈,避免空栈调用
pop()抛出异常; - 最终栈为空的判断:确保所有左括号都有对应的右括号匹配(比如
"({"遍历完栈不为空,返回 false)。
三、队列的典型应用:二叉树层次遍历
1. 问题场景
二叉树的层次遍历(广度优先遍历 / BFS):按从上到下、从左到右的顺序,逐层输出二叉树的节点值。
例如:
java
1
/ \
2 3
/ \ \
4 5 6
层次遍历结果:[1, 2, 3, 4, 5, 6](或按层分组:[[1], [2,3], [4,5,6]])。
2. 解题思路
- 初始化队列,将根节点入队;
- 循环处理队列:
- 记录当前队列的大小(即当前层的节点数);
- 遍历当前层的所有节点:弹出队首节点,记录值;
- 若节点有左子节点,入队;若有右子节点,入队;
- 直到队列为空,结束遍历。
3. 代码实现(Java)
java
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
/**
* 队列实现二叉树层次遍历(按层输出)
*/
// 首先定义二叉树节点类
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) { val = x; }
}
public class LevelOrderTraversal {
public static List<List<Integer>> levelOrder(TreeNode root) {
// 结果集合:每个子List存储一层的节点值
List<List<Integer>> result = new ArrayList<>();
// 边界条件:根节点为空,直接返回空集合
if (root == null) {
return result;
}
// 1. 初始化队列,根节点入队
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
// 2. 循环处理队列
while (!queue.isEmpty()) {
// 当前层的节点数(关键:队列当前的大小就是当前层的节点数)
int levelSize = queue.size();
// 存储当前层的节点值
List<Integer> currentLevel = new ArrayList<>();
// 3. 遍历当前层的所有节点
for (int i = 0; i < levelSize; i++) {
// 弹出队首节点
TreeNode node = queue.poll();
// 记录当前节点值
currentLevel.add(node.val);
// 4. 左子节点入队(非空才入队)
if (node.left != null) {
queue.offer(node.left);
}
// 右子节点入队(非空才入队)
if (node.right != null) {
queue.offer(node.right);
}
}
// 将当前层的结果加入总结果
result.add(currentLevel);
}
return result;
}
// 测试用例:构建示例二叉树并测试
public static void main(String[] args) {
// 构建二叉树
TreeNode root = new TreeNode(1);
root.left = new TreeNode(2);
root.right = new TreeNode(3);
root.left.left = new TreeNode(4);
root.left.right = new TreeNode(5);
root.right.right = new TreeNode(6);
// 层次遍历
List<List<Integer>> result = levelOrder(root);
// 输出结果:[[1], [2, 3], [4, 5, 6]]
System.out.println(result);
}
}
4. 代码关键解释
TreeNode类:二叉树节点的标准定义,包含值、左子节点、右子节点;levelSize = queue.size():每次循环先记录当前队列大小,确保只处理当前层的节点(避免跨层);- 子节点入队判断:只将非空的子节点入队,避免处理空节点;
- 结果按层存储:
List<List<Integer>>结构更直观体现「层次」特性,也是面试中常见要求。
四、总结
关键点回顾
- 核心特性 :栈是「后进先出(LIFO)」,队列是「先进先出(FIFO)」,Java 中推荐用
Deque实现栈、LinkedList实现队列; - 栈的核心应用:括号匹配的关键是「左括号入栈,右括号匹配栈顶」,遍历结束后栈必须为空;
- 队列的核心应用 :二叉树层次遍历的关键是「按层处理队列」,通过
queue.size()控制每层的节点数,实现逐层输出。