|
@@ -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) |
|