Просмотр исходного кода

feat: 新增练习4——LinkedList栈操作(Stack),模拟浏览器后退功能,涵盖push/pop/peek及LIFO特性

WanJL 3 недель назад
Родитель
Сommit
d2c8dc73a7
1 измененных файлов с 129 добавлено и 2 удалено
  1. 129 2
      课堂练习题/20260730-LinkedList双链表练习.md

+ 129 - 2
课堂练习题/20260730-LinkedList双链表练习.md

@@ -1,8 +1,8 @@
 # 2026-07-30 LinkedList 双链表专项练习(课堂练习用)
 # 2026-07-30 LinkedList 双链表专项练习(课堂练习用)
 
 
-> **说明**:以下习题围绕今日授课核心内容——**LinkedList 双链表** 展开,涵盖 LinkedList 的特点与底层结构、LinkedList 作为 List 的基本操作、LinkedList 作为 Deque/Queue 的双端队列/队列操作、手写简单链表等知识点。
+> **说明**:以下习题围绕今日授课核心内容——**LinkedList 双链表** 展开,涵盖 LinkedList 的特点与底层结构、LinkedList 作为 List 的基本操作、LinkedList 作为 Deque/Queue/Stack 的双端队列/队列/栈操作、手写简单链表等知识点。
 >
 >
-> 题目分为 **基础练习**(LinkedList 基本操作,3 题)和 **进阶挑战**(链表应用,2 题)。
+> 题目分为 **基础练习**(LinkedList 基本操作,4 题)和 **进阶挑战**(链表应用,2 题)。
 
 
 ---
 ---
 
 
@@ -210,6 +210,95 @@ public class QueueDemo {
 
 
 ---
 ---
 
 
+## 练习 4:LinkedList 的栈操作(Stack)
+
+**难度**:⭐⭐  
+**知识点**:`push()` / `pop()` / `peek()`、LIFO(后进先出)栈特性、`Deque` 接口的栈语义
+
+**场景描述**:LinkedList 实现了 `Deque` 接口,而 `Deque` 提供了完整的**栈(Stack)操作**。Java 官方建议:**使用 `Deque` 代替 `Stack` 类**,因为 `Stack` 继承自 `Vector`,性能较差且线程安全开销不必要。模拟一个"浏览器后退"功能——每次访问新页面就入栈,点击后退就从栈顶弹出。
+
+**题目要求**:
+
+在 `StackDemo` 类的 `main` 方法中,完成以下操作:
+
+```java
+import java.util.Deque;
+import java.util.LinkedList;
+
+public class StackDemo {
+    public static void main(String[] args) {
+        // 1. 创建一个 Deque<String>,使用 LinkedList 实现,作为栈使用
+        //    提示:Deque<String> stack = new LinkedList<>();
+
+        // 2. 模拟小明浏览网页:
+        //    "首页" → push() 入栈
+        //    "新闻页" → push() 入栈
+        //    "体育频道" → push() 入栈
+        //    "NBA战报" → push() 入栈
+
+        // 3. 查看当前栈顶页面(peek()),不弹出
+        //    输出:"当前页面:NBA战报"
+
+        // 4. 点击后退:依次弹出 3 次(pop())
+        //    每次输出:"后退到:XXX"
+        //    顺序应该是:NBA战报 → 体育频道 → 新闻页
+
+        // 5. 输出栈中剩余页面
+        //    输出:"剩余浏览记录:[首页]"
+
+        // 6. 小明又访问了新页面:"科技频道" → push()
+        //    "AI专题" → push()
+
+        // 7. 再次查看栈顶(peek())
+
+        // 8. 连续弹出所有页面(用循环 pop() 直到栈为空)
+        //    提示:while (!stack.isEmpty()) { ... }
+
+        // 9. 尝试在空栈时调用 pop(),观察发生了什么
+        //    提示:NoSuchElementException(与 Queue 的 poll() 不同)
+    }
+}
+```
+
+**预期输出**:
+```
+========== 浏览器后退模拟 ==========
+当前页面:NBA战报
+后退到:NBA战报
+后退到:体育频道
+后退到:新闻页
+剩余浏览记录:[首页]
+当前页面:AI专题
+连续后退:
+后退到:AI专题
+后退到:科技频道
+后退到:首页
+浏览记录已清空
+```
+
+**关键方法说明**:
+
+| 方法 | 栈的等价操作 | 说明 |
+|------|-------------|------|
+| `push(E e)` | 入栈(压栈) | 将元素压入栈顶,等价于 `addFirst()` |
+| `pop()` | 出栈(弹栈) | 移除并返回栈顶元素,空栈时抛 `NoSuchElementException` |
+| `peek()` | 查看栈顶 | 返回栈顶元素但不移除,空栈时抛 `NoSuchElementException` |
+
+**LinkedList 三种角色对比**:
+
+| 角色 | 接口 | 核心操作 | 数据结构 |
+|------|------|---------|---------|
+| 列表(List) | `List` | `add()` / `get()` / `set()` / `remove(index)` | 线性表 |
+| 队列(Queue) | `Queue` | `offer()` / `poll()` / `peek()` | FIFO 先进先出 |
+| 栈(Stack) | `Deque` | `push()` / `pop()` / `peek()` | LIFO 后进先出 |
+
+**思考题**:
+1. Java 官方为什么不推荐使用 `java.util.Stack` 类?`Stack` 继承自 `Vector`(线程安全)带来了什么问题?
+2. `Deque` 的 `push()` 和 `addFirst()` 本质上是同一个方法,为什么提供两个名字?
+3. 用 `Deque` 实现栈时,`pop()` 和 `poll()` 在空栈时行为有何不同?(提示:异常 vs null)
+
+---
+
 # 第二部分:进阶挑战
 # 第二部分:进阶挑战
 
 
 > 以下练习综合运用 LinkedList、链表数据结构、手写链表等知识。
 > 以下练习综合运用 LinkedList、链表数据结构、手写链表等知识。
@@ -569,6 +658,43 @@ System.out.println("空队列 poll() 返回:" + emptyQueue.poll());
 System.out.println("空队列 peek() 返回:" + emptyQueue.peek());
 System.out.println("空队列 peek() 返回:" + emptyQueue.peek());
 ```
 ```
 
 
+## 练习 4 参考
+
+```java
+Deque<String> stack = new LinkedList<>();
+
+stack.push("首页");
+stack.push("新闻页");
+stack.push("体育频道");
+stack.push("NBA战报");
+
+System.out.println("当前页面:" + stack.peek());
+
+System.out.println("后退到:" + stack.pop());
+System.out.println("后退到:" + stack.pop());
+System.out.println("后退到:" + stack.pop());
+
+System.out.println("剩余浏览记录:" + stack);
+
+stack.push("科技频道");
+stack.push("AI专题");
+System.out.println("当前页面:" + stack.peek());
+
+System.out.println("连续后退:");
+while (!stack.isEmpty()) {
+    System.out.println("后退到:" + stack.pop());
+}
+System.out.println("浏览记录已清空");
+
+// 空栈异常演示
+Deque<String> emptyStack = new LinkedList<>();
+try {
+    emptyStack.pop();  // 抛出 NoSuchElementException
+} catch (Exception e) {
+    System.out.println("空栈调用 pop():" + e.getClass().getSimpleName());
+}
+```
+
 ## 进阶 1 参考(核心代码)
 ## 进阶 1 参考(核心代码)
 
 
 ```java
 ```java
@@ -659,6 +785,7 @@ public boolean contains(N value) {
 | LinkedList 创建与 List 操作 | 练习 1 | `add()` / `get()` / `set()` / `remove(index)` / `size()` |
 | LinkedList 创建与 List 操作 | 练习 1 | `add()` / `get()` / `set()` / `remove(index)` / `size()` |
 | LinkedList 双端队列操作 | 练习 2 | `addFirst()` / `addLast()` / `getFirst()` / `getLast()` / `removeFirst()` / `removeLast()` |
 | LinkedList 双端队列操作 | 练习 2 | `addFirst()` / `addLast()` / `getFirst()` / `getLast()` / `removeFirst()` / `removeLast()` |
 | LinkedList 队列操作 | 练习 3 | `offer()` / `poll()` / `peek()`、FIFO 特性 |
 | LinkedList 队列操作 | 练习 3 | `offer()` / `poll()` / `peek()`、FIFO 特性 |
+| LinkedList 栈操作 | 练习 4 | `push()` / `pop()` / `peek()`、LIFO 特性、Deque 代替 Stack |
 | LinkedList vs ArrayList 性能对比 | 进阶 1 | 头部插入 O(1) vs O(n)、随机访问 O(n) vs O(1)、遍历方式 |
 | LinkedList vs ArrayList 性能对比 | 进阶 1 | 头部插入 O(1) vs O(n)、随机访问 O(n) vs O(1)、遍历方式 |
 | 手写单链表增删改查 | 进阶 2 | 泛型、Node 节点、`get(index)`、`remove(index)`、`contains()`、`clear()` |
 | 手写单链表增删改查 | 进阶 2 | 泛型、Node 节点、`get(index)`、`remove(index)`、`contains()`、`clear()` |
 | 链表节点结构 | 进阶 2 | 单链表 Node(value + next)vs 双链表 Node(item + next + prev) |
 | 链表节点结构 | 进阶 2 | 单链表 Node(value + next)vs 双链表 Node(item + next + prev) |