CS61B 课程笔记(Lecture 04)
1. SLList简介
SLList(单向链表)是一种动态数据结构,允许高效地插入和检索元素。与IntList(静态列表)相比,SLList提供了更灵活的操作接口。
2. SLList的基本结构
IntNode类
- 表示链表中的一个节点,包含一个整数项和对下一个节点的引用。
1 2 3 4 5 6 7 8 9 10
| public class IntNode { public int item; public IntNode next;
public IntNode(int i, IntNode n) { item = i; next = n; } }
|
SLList类
- 维护对第一个节点的引用,并提供方法以进行列表操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class SLList { public IntNode first;
public SLList(int x) { first = new IntNode(x, null); }
public void addFirst(int x) { first = new IntNode(x, first); }
public int getFirst() { return first.item; } }
|
示例用法
1 2 3 4
| SLList L = new SLList(15); L.addFirst(10); L.addFirst(5); int x = L.getFirst();
|
3. SLList与IntList的比较
- IntList
是一种更简单的实现,变量可能直接指向列表的不同部分。
- SLList
通过提供抽象,作为与数据结构的中介,简化了用户的交互。
4. Java中的访问控制
- 使用
private
关键字限制对类成员的访问,有助于隐藏实现细节,并简化用户与类的交互。
限制访问的好处:
- 减少用户的认知负担。
- 允许在不影响用户的情况下进行内部更改。
- 公共成员意味着需要承诺维持其行为。
5. 嵌套类
- 嵌套类有助于组织代码,当一个类在逻辑上属于另一个类时特别有用。
使用嵌套类的情况:
- 当嵌套类从属于外部类时。
- 封装不需要在外部访问的功能。
静态嵌套类
- 如果嵌套类不访问外部类的任何实例变量或方法,则应声明为
static
。
1 2 3 4 5 6 7 8 9 10 11
| public class SLList { public static class IntNode { public int item; public IntNode next;
public IntNode(int i, IntNode n) { item = i; next = n; } } }
|
6. 使用缓存管理大小
- 维护一个大小变量来缓存列表的大小,使得大小查询的时间复杂度为O(1)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class SLList { private IntNode first; private int size;
public SLList(int x) { first = new IntNode(x, null); size = 1; }
public void addFirst(int x) { first = new IntNode(x, first); size += 1; }
public int size() { return size; } }
|
7. 哨兵节点
- 哨兵节点通过消除特殊情况(例如,空列表处理)来简化链表操作。
哨兵节点的实现
- 创建一个始终存在的哨兵节点,可以简化许多链表操作,使代码更简单易懂。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| public class SLList { private IntNode sentinel;
public SLList() { sentinel = new IntNode(0, null); sentinel.next = sentinel; }
public void addFirst(int x) { IntNode newNode = new IntNode(x, sentinel.next); sentinel.next = newNode; }
public int getFirst() { if (sentinel.next == sentinel) { throw new RuntimeException("List is empty!"); } return sentinel.next.item; }
public int size() { int count = 0; IntNode current = sentinel.next; while (current != sentinel) { count++; current = current.next; } return count; } }
|
哨兵节点的好处:
- 简化代码逻辑,减少对空指针的检查。
- 维护不变式,确保在任何时候,哨兵节点的引用始终指向有效的节点。