编程自学指南:java程序设计开发,Java Stack 栈详解课件(初学者版)
一、课程信息
学习目标
- 理解栈的核心特性(LIFO)与生活场景
- 掌握 Stack 的 5 大核心方法(push/pop/peek/empty/search)
- 能通过栈解决实际问题(括号匹配、撤销操作)
- 区分 Stack 与 Deque 的使用场景,避免误用
二、课程导入:生活中的栈
🌰 生活案例:弹夹与盘子
- 弹夹:最后压入的子弹最先射出(LIFO)
- 餐厅叠盘:最后放的盘子最先被拿走
- 书包书本:后塞的书先被取出
💻 代码场景:撤销操作
// 模拟文本编辑器的撤销(Undo)功能
Stack<String> undoStack = new Stack<>();
undoStack.push("插入'Hello'");
undoStack.push("删除最后一个字符");
// 撤销:执行 undoStack.pop() 回到上一步
📌 核心特性:后进先出(Last In First Out)
三、Stack 基础:核心方法与案例
1. 继承关系与初始化
import java.util.Stack;
Stack<String> stack = new Stack<>(); // 继承自Vector,线程安全(但推荐用Deque)
2. 五大核心方法(带案例)
方法名 | 作用 | 案例(以字符串栈为例) | 注意事项 |
---|---|---|---|
push(E e) | 压栈(顶部添加元素) | stack.push("A"); | 返回压入的元素 |
pop() | 弹栈(移除并返回顶部元素) | String top = stack.pop(); | 空栈调用抛EmptyStackException |
peek() | 查看顶部元素(不弹出) | String peek = stack.peek(); | 空栈调用抛EmptyStackException |
empty() | 判断栈是否为空 | if (stack.empty()) {...} | |
search(E e) | 查找元素位置(顶部为 1) | int pos = stack.search("B"); | 不存在返回 - 1 |
3. 基础操作案例
Stack<Integer> nums = new Stack<>();
nums.push(10); // 栈:[10]
nums.push(20); // 栈:[10, 20]
System.out.println(nums.pop()); // 20(栈:[10])
System.out.println(nums.peek()); // 10(栈不变)
System.out.println(nums.search(10)); // 1(顶部元素位置)
四、栈的典型应用场景
案例 1:括号匹配(经典算法)
需求:判断字符串中括号是否匹配("()[]{}"
)
思路:遇到左括号压栈,遇到右括号弹栈并检查是否匹配
public static boolean isValid(String s) {
Stack<Character> stack = new Stack<>();
for (char c : s.toCharArray()) {
if (c == '(' || c == '[' || c == '{') {
stack.push(c);
} else {
if (stack.empty()) return false; // 右括号多余
char top = stack.pop();
if ((c == ')' && top != '(') ||
(c == ']' && top != '[') ||
(c == '}' && top != '{')) {
return false; // 括号不匹配
}
}
}
return stack.empty(); // 左括号不能多余
}
// 测试:
System.out.println(isValid("()[]{}")); // true
System.out.println(isValid("([)]")); // false
案例 2:逆序字符串
需求:将 "abc" 逆序为 "cba"
思路:压栈后依次弹出
public static String reverse(String str) {
Stack<Character> stack = new Stack<>();
for (char c : str.toCharArray()) {
stack.push(c);
}
StringBuilder reversed = new StringBuilder();
while (!stack.empty()) {
reversed.append(stack.pop());
}
return reversed.toString();
}
// 输出:reverse("hello") → "olleh"
案例 3:浏览器历史记录(Undo/Redo)
需求:模拟浏览器的前进 / 后退
实现:两个栈(后退栈 + 前进栈)
Stack<String> backStack = new Stack<>();
Stack<String> forwardStack = new Stack<>();
// 访问页面A→B→C
backStack.push("A");
backStack.push("B");
backStack.push("C");
// 点击后退到B
String current = backStack.pop(); // C
forwardStack.push(current); // 前进栈存C
current = backStack.peek(); // B
// 点击前进到C
current = forwardStack.pop(); // C
backStack.push(current);
五、Stack vs Deque:选择与避坑
1. 核心区别
特性 | Stack(旧) | Deque(推荐) |
---|---|---|
继承 | 继承 Vector,线程安全 | 接口,实现类(如 ArrayDeque) |
方法 | push/pop/peek(专属) | addFirst/removeFirst 等 |
线程安全 | 是(但性能低) | 否(需手动同步) |
推荐场景 | 教学演示 | 实际开发(替代 Stack) |
2. 推荐写法:用 Deque 实现栈
Deque<String> stack = new ArrayDeque<>(); // 更高效
stack.push("A"); // 等价 addFirst()
stack.pop(); // 等价 removeFirst()
3. 初学者误区
- 误区 1:认为 Stack 是唯一栈实现(实际 Deque 更优)
- 误区 2:忽略空栈检查(
pop()
/peek()
前先用empty()
) - 误区 3:在多线程中直接使用 Stack(应优先用
ConcurrentLinkedDeque
)
六、内存分配与性能
1. 栈的内存模型(对比堆)
Stack<Integer> stack = new Stack<>(); // 栈引用在栈区,对象在堆区
stack.push(10); // 10是Integer对象,存储在堆区
图解:栈变量存堆地址,元素是堆中的对象(与数据结构的 “栈” 无关)。
2. 性能测试:压栈 10 万次
// Stack(旧)
long start = System.currentTimeMillis();
Stack<Integer> s = new Stack<>();
for (int i=0; i<100000; i++) s.push(i);
System.out.println("Stack耗时:" + (System.currentTimeMillis()-start)); // 约12ms
// Deque(推荐)
start = System.currentTimeMillis();
Deque<Integer> d = new ArrayDeque<>();
for (int i=0; i<100000; i++) d.push(i);
System.out.println("Deque耗时:" + (System.currentTimeMillis()-start)); // 约3ms
七、课堂练习
练习 1:简易计算器(后缀表达式)
需求:计算 "3 4 +" → 7(用栈实现)
提示:数字压栈,遇到运算符弹栈计算
Stack<Integer> nums = new Stack<>();
String[] tokens = "3 4 +".split(" ");
for (String token : tokens) {
if (isNumber(token)) {
nums.push(Integer.parseInt(token));
} else {
int b = nums.pop();
int a = nums.pop();
nums.push(calculate(a, b, token));
}
}
System.out.println(nums.pop()); // 7
练习 2:有效括号扩展(含嵌套)
输入:"{[()()]}"
输出:true
提示:嵌套括号需严格匹配(用栈记录左括号)
八、课程总结
知识图谱:
栈 → LIFO特性 → push压栈,pop弹栈
↳ 典型应用:括号匹配、逆序、撤销
↳ 推荐写法:Deque替代Stack(高效)
↳ 避坑:空栈检查、线程安全
口诀记忆:
“栈似弹夹后入先出,push 压 pop 弹要记牢,
括号匹配栈来帮忙,Deque 替代性能强!”
九、课后作业(必做 + 选做)
必做 1:用栈实现十进制转二进制
输入:10
输出:1010
提示:除 2 取余,结果逆序(栈天然逆序)
必做 2:括号匹配升级(含多种括号)
输入:"({[]})[()]"
输出:true
选做:汉诺塔问题(递归 + 栈模拟)
需求:用栈模拟汉诺塔移动过程(提示:三个栈代表三根柱子)