编程自学指南:java程序设计开发,Java Stack 栈详解,理解栈的核心特性(LIFO),掌握 Stack 的 5 大核心方法,区分 Stack 与 Deque

编程自学指南:java程序设计开发,Java Stack 栈详解课件(初学者版)

一、课程信息

学习目标

  1. 理解栈的核心特性(LIFO)与生活场景
  2. 掌握 Stack 的 5 大核心方法(push/pop/peek/empty/search)
  3. 能通过栈解决实际问题(括号匹配、撤销操作)
  4. 区分 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

选做:汉诺塔问题(递归 + 栈模拟)

需求:用栈模拟汉诺塔移动过程(提示:三个栈代表三根柱子)

### LIFO 特性的具体含义 LIFO(Last In First Out,后进先出)是一种常见的数据处理原则,表示最后进入的数据最先被取出。这种特性使得成为一种典型的实现 LIFO 原则的数据结构[^1]。在中,所有的插入(压,Push)和删除(弹,Pop)操作都发生在同一端——即顶。 #### 的基本原理 是一种线性数据结构,其特点是仅允许在一端进行操作。这一端被称为顶,而另一端固定不动,称为底。的操作主要包括两种基本形式: - **压(Push)**:将新元素添加到顶。 - **弹(Pop)**:移除并返回当前顶的元素[^3]。 由于这些操作均针对顶执行,因此最近加入中的元素会优先被访问或移除,这正是 LIFO 原则的核心体现。 --- ### LIFO 特性的典型应用场景 LIFO 特性因其独特的数据存取方式,在许多实际场景中有广泛应用: #### 1. 函数调用管理 在程序运行过程中,每当遇到函数调用时,系统都会将其上下文信息(如参数、局部变量等)保存在一个名为“调用”的特殊中。当函数完成执行后,系统会从中弹出对应的上下文信息,恢复之前的执行状态。这种方式利用了 LIFO 的特点,确保每次退出的是最后一个被调用的函数[^1]。 #### 2. 表达式求值转换 对于算术表达式的计算或者不同形式之间的转换(如中缀转后缀),可以通过来高效地管理和存储中间结果。例如,在解析中缀表达式的过程中,运算符和操作数按照特定规则依次入和出,最终得到正确的计算结果。 #### 3. 括号匹配检测 验证字符串中的括号是否正确配对是一个经典的编程问题。通过使用,可以方便地跟踪未闭合的左括号数量及其位置。每遇到右括号时,则尝试从中弹出相应的左括号;如果无法成功匹配,则说明存在错误[^1]。 #### 4. 替代递归优化性能 某些情况下,直接采用递归来解决问题可能会带来较大的内存开销甚至引发溢出风险。此时可考虑手动模拟递归过程,借助显式定义的代替隐含使用的系统调用。以 Fibonacci 数列为例,传统递归方法容易造成重复计算,效率低下;然而改用迭代配合辅助的方式能够显著改善这种情况[^2]。 以下是基于 Java 实现的一个简单案例演示如何运用堆输出斐波那契序列的部分项次序关系: ```java import java.util.*; class StackExample { public static void main(String[] args) { Stack<Integer> stack = new Stack<>(); // 初始化前两个数值为1 stack.push(1); stack.push(1); int count = 1; while (count <= 10) { for (int i = 1; i <= 2; ++i) { Integer topValue = stack.pop(); Integer secondTopValue = stack.pop(); int sum = topValue + secondTopValue; System.out.println(sum); // 输出当前项 // 将旧值重新推回内以便后续继续累加 stack.push(secondTopValue); stack.push(topValue); // 新增总和作为下一轮的基础之一 stack.push(sum); ++count; } } } } ``` 此代码片段展示了如何利用自定义维护的 `stack` 对象逐步构建起完整的 Fibonnaci 序列成员列表,并且完全规避掉了原始纯递归版本可能存在的诸多弊端之处。 --- ### 总结 综上所述,LIFO 不仅仅是理论上的抽象概念,更是在众多领域发挥着重要作用的实际工具。无论是日常开发还是深入研究算法设计模式之时,深刻理解并熟练掌握此类基础知识点都将大有裨益。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zl515035644

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值