CS61B 课程笔记(Lecture 26 Midterm 2 Review)
CS61B 课程笔记(Lecture 26 Midterm 2 Review)
1. 复杂度分析
问题描述:
分析以下常见数据结构操作的时间复杂度,假设 (N) 是项的数量。主要分析的数据结构包括:
- 数组
- 链表
- 哈希表
- 平衡二叉搜索树(如 AVL 树、红黑树)
以下是各操作的时间复杂度分类:
数据结构操作:
1. 数组
.get
操作:- 最佳情况: (\(Θ(1)\)) — 直接通过索引访问目标元素。
- 最坏情况: (\(Θ(1)\)) — 数组是随机访问的,不会影响时间复杂度。
.put
操作:- 最佳情况: (\(Θ(1)\)) — 在空位置直接插入元素。
- 最坏情况: (\(Θ(N)\)) — 在数组满的情况下,可能需要扩展数组并复制元素。
2. 链表
.get
操作:- 最佳情况: (\(Θ(1)\)) — 当目标元素在头节点时。
- 最坏情况: (\(Θ(N)\)) — 需要遍历整个链表找到目标元素。
.put
操作:- 最佳情况: (\(Θ(1)\)) — 在头节点插入元素。
- 最坏情况: (\(Θ(1)\)) — 直接插入在尾部或在中间节点(若有尾指针)。
3. 哈希表
.get
操作:- 最佳情况: (\(Θ(1)\)) — 根据键直接访问值。
- 最坏情况: (\(Θ(N)\)) — 当所有元素发生冲突并链入同一桶时。
.put
操作:- 最佳情况: (\(Θ(1)\)) — 直接插入到空位置。
- 最坏情况: (\(Θ(N)\)) — 可能需要处理冲突,或者在最坏情况下重新哈希整个表。
4. 平衡二叉搜索树(如 AVL 树、红黑树)
.get
操作:- 最佳情况: (\(Θ(1)\)) — 直接找到根节点。
- 最坏情况: (\(Θ(\log N)\)) — 树的高度为 (\(O(\log N)\))。
.put
操作:- 最佳情况: (\(Θ(1)\)) — 插入在空位置或简单情况。
- 最坏情况: (\(Θ(\log N)\)) — 可能需要进行树的重平衡。
2. 优先级队列和堆
a) 优先级队列的 remove
方法:
问题描述:
设计一个有效的优先级队列的 remove
方法,使得最小值的访问时间为 (\(O(1)\))。假设优先级队列底层实现为最小堆。
解答:
1 | public Comparable remove() { |
- 步骤解析:
- 首先,获取最小值(根节点)并保存到
result
变量中。 - 然后,将最后一个元素替换到根节点,以保持堆的结构。
- 移除最后一个元素。
- 通过调用
heapifyDown(0)
方法,从根节点开始调整堆,以确保最小堆性质得以恢复。
- 首先,获取最小值(根节点)并保存到
b) 时间复杂度比较:
问题描述:
比较上述 remove
方法的时间复杂度与使用
Collections.sort
的时间复杂度。
解答:
remove
方法的时间复杂度:- 获取最小值的时间为 (\(O(1)\))。
- 交换根节点与最后一个元素的时间为 (\(O(1)\))。
- 移除最后一个元素的时间为 (\(O(1)\))。
heapifyDown(0)
的时间复杂度为 (\(O(\log M)\)),其中 (\(M\)) 是当前堆中的元素数量。- 因此,
remove
方法的总时间复杂度为 (\(O(\log M)\))。
Collections.sort
方法的时间复杂度:- 使用 Java 的
Collections.sort
方法,通常基于归并排序或快速排序,其时间复杂度为 (\(O(M \log M)\))。
- 使用 Java 的
- 比较结果:
- 因此,在处理优先级队列的最小值移除操作时,使用最小堆的
remove
方法在单次操作时的时间复杂度为 (\(O(\log M)\)),而使用Collections.sort
方法的时间复杂度为 (\(O(M \log M)\))。 - 在需要频繁进行删除操作的情况下,优先级队列的实现效率更高。
- 因此,在处理优先级队列的最小值移除操作时,使用最小堆的
3. 图
b) 深度优先搜索(DFS)伪代码:
问题描述:
编写 DFS 算法的伪代码,并注释返回/重置条件。
解答:
1 | DFS(node): |
- DFS 的基本思想是从一个节点开始,访问它并标记为已访问,然后递归访问所有未访问的邻居节点。
c) 有向图边数计算:
问题描述:
计算一个包含 6 个顶点的有向图的边数,假设每个顶点与其他 5 个顶点相连(无自环)。
解答:
- 边数 = (\(6 \times 5 = 30\))。
4. 广度优先搜索(BFS)
a) BFS 伪代码:
问题描述:
编写 BFS 算法的伪代码,并解释其过程。
解答:
1 | BFS(startNode): |
- BFS 从起始节点开始,将其标记为已访问并入队,然后重复从队列中出队节点,处理该节点并将其未访问的邻居入队,直到队列为空。
b) 处理已访问节点:
问题描述:
在图的 BFS 遍历中,如何处理已经访问过的节点?
解答:
- 在实现过程中,需要在访问每个节点时进行标记,避免重复访问,确保每个节点只被处理一次。
c) 绘制 BFS 迭代状态:
问题描述:
在图中进行 BFS 时,绘制每次迭代后的状态(可视化过程)。
解答:
- 可以绘制每一步入队和出队的节点,清楚展示 BFS 处理的顺序。
d) 添加边的影响:
问题描述:
如果在 BFS 的输入图中添加一条边,是否会影响算法的时间复杂度?请解释。
解答:
- 添加边不会影响算法的时间复杂度,仍然为 (\(O(V + E)\)),其中 (\(V\)) 是顶点数,(\(E\)) 是边数。
5. 代码重构
问题描述:
重构以下代码段,以减少嵌套层次并提高可读性:
1 | public void processValues(int a, int b) { |
解答:
重构后的代码如下:
1 | public void processValues(int a, int b) { |
- 在重构后的代码中,使用了一个辅助方法
processA
来处理a
的情况,减少了嵌套层次,增强了代码的可读性。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Totoroの旅!
评论