cs61b-sp21 笔记
作者 : Kumikosoli
package(包)
- 把多个类封装在一个包中,用于完成共同的功能。
意义
如org.junit.Assert.assertArrayEquals中,org.junit是包,Assert是类名,assertArrayEquals 是方法名
用法
在每个类文件开头加上 package package_name 表示这个类在包中
引用
- java中,除八种基本类型外,其他类型都是作为引用被初始化,引用变量存储的是指向结构的地址
使用junit进行test
- 使用
org.junit.Assert.assertArrayEquals(expected,input)验证输入与期望的数组是否相等
Debug工具
- conditional breakpoint:条件断点,当且仅当在该断点处满足条件为True时才会停止
- resume:恢复程序,使程序继续运行直到回到断点
files
通过File constructor,可以在Java中生成file对象File f = new File("dummy.txt");这并不会实际创造文件dummy.txt。要生成时可以使用f.createNewFile();。可以用f.exists()检测文件是否存在。
file对象也可以代表文件目录。要生成该目录时使用d.mkdir()
链表Linklist
-
nested class:嵌套类,在类中定义一个类。在list类中定义node类,把初始化的list作为头节点。
-
为链表提供头节点可简化添加元素时的代码。
-
对于双向链表,可添加头尾两个哨兵节点或者将链表设置为环,简化删除、添加节点等代码。
-
类中可将成员类型推迟到初始化或声明中决定。具体代码
1 2 3 4 5 6 7 8 9 10public class Sllist<BleepBLORP>{ private Node sentinel private int size public class Node{ public BleepBlorp item; public Node next; ………… } } …………使用时在初始化对象时语法
Sllist<String> lt1 = new Sllist<>("Hi!")<>中的泛型要使用引用类型。 对应关系:基本类型 引用 double Double int Integer char Character boolean Boolean long Long
类继承
implement
形如public class RotatingSLList<Item> implement SLList<Item>的类定义,用于表示类继承,子类拥有父类中定义的所有方法(一般要在子类中写出继承的接口的实现)。也可以用父类中已定义好的方法。
- 哪怕父类中已经定义了方法,也可以在子类中重写。在方法上方添加
@override显式表示这是继承自父类的方法并给出定义。
extends
形如public class RotatingSLList<Item> extends SLList<Item>的类定义,表示子类继承了父类的所有非private成员(属性、方法、嵌套类),可以直接使用,并且可以在子类中添加新的方法等。
- extends创建的子类,在调用构造函数时首先调用父类的构造函数,然后再回到子类新的构造函数中。
- 可以显式调用父类构造函数
super(),若不显式调用,编译器会自动完成。
interface
与类相似,定义类似public interface Mycontrast{……} ,但其中只能包含子类需要实现的方法,并在子类中给出定义。
子类通过implement继承interface,一个子类可以继承多个interface。
高阶函数
java没有函数指针,无法直接传递函数。想要使用类似函数指针的效果,可以通过interface和类继承,让子类implement同一个接口override接口的方法,并将子类传入函数中充当函数指针作用。例如
|
|
java库 java.util
内含多种数据结构
List interface
具有子类Arraylist
hashset
java的set interface 的子类
Sets
存储不重复元素的无序集合。
方法
add()加入元素contain()检测是否包含元素size()返回集合大小
异常处理
throw 关键字
- 使用格式
throw new ExceptionObject(parameter1, ...) - 例子:
|
|
new 后的是异常类;括号中的是对异常的解释。
Iteration
|
|
这是对for循环的强化使用。其具体为:
|
|
因此,若想在自己定义的类中使用迭代器,则需要让此类extend Iterable<T>``iteratior()方法返回一个iterator对象。该对象应在类中定义私有嵌套类(定义hasNext方法和next方法),此时就可以使用第二个的具体形式。
如果想要使用简洁的for(T city : s)形式,则需要给嵌套的Iterator类加上implement Iterator()
Object
所有类都隐式继承了Object类,拥有其定义的方法。常用的为String tostring()和boolean equals(Object obj)。一般需要在自己定义的类中override这些方法
toString
在发生类到String的类型转换时会隐性调用。默认实现是返回类存储的地址。
- 由于使用 String a=a+“qqq"时会产生新的字符串,时间复杂度为$O(n)$,Java新增了
StringBuilder类使拼接字符串的时间复杂度为 $ O(1) $。
使用StringBuilder类凭借字符串代码如下:
|
|
equals
默认对比两个对象地址是否相同(等价于==),返回True/False.
通过重写可以比较对象不同的属性。
Data Structure 部分
时间复杂度
$Θ(f(n))$
代表时间复杂度与 $f(n)$ 同阶
$O(f(n))$
代表时间复杂度与 $f(n)$ 同阶或更低阶
抽象数据类型(ADT)
仅有操作而不由具体实现定义的数据类型。如Deque是一个ADT,可以由数组或者链表实现。不同的实现对应的操作是相同的。
常见ADT
Stacks
实现后进先出的数据类型
操作
push(int x)int pop()
Lists
有序列表
操作
add(int i)int get(int i)
Sets
无序无重复元素集合
操作
add(int i)contains(int i)
Maps
键值对集合
操作
put(k key,V value)V get(K key)
不相交集(disjoint set)
用于在一个无重复元素集合中跟踪两个元素是否在同一个集合中,以及合并两个元素所在的集合。
基本功能
connect(x, y)合并x y 所在的集合isConnected(x, y)当x,y在同一个集合中时,返回true,否则返回false
实现方法
-
使用n个元素数组arr存储元素0 ~ n-1的父元素,当元素没有父元素时,这个元素为根,数组下标对应的值为-k(k为根对应的集合下元素数量).
-
connect(x, y):对x,y向上遍历,找到x和y分别对应的根,并将根链接。 -
isConnected(x, y):对x,y向上遍历,查看x和y对应的根节点是否相同 -
加权快速联合:每次调用
connect(x, y)时,把集合元素较少的根的父元素改为另一个根,并修改另一个根存储的集合元素数量. -
路径压缩:每次调用
isConnected(x, y)时,将遍历到的节点的父元素全部变为根节点。
使用加权快速联合和路径压缩下的操作时间复杂度
| Constructor | connect | isConnected |
|---|---|---|
| Θ(N) | O(log* N) | O(log* N) |
二叉搜索树(Binary Search Trees)
包含节点(存储值与指向左右子节点的边)
满足性质
- 左子树所有键值小于其根节点键值
- 右子树所有键值大于其根节点键值
- 左、右子树都是二叉搜索树
定义
|
|
操作
static BST find(BST T, Key sk)
|
|
static BST insert(BST T, Key ik)
|
|
static void Delete(key ik)删除一个节点需要考虑节点孩子的情况。
-
没有孩子节点:直接删除节点
-
一个孩子节点:让父节点指向被删除节点的边指向被删除节点的孩子节点
-
两个孩子节点:找到左子树最大/右子树最小的节点,交换其与被删除节点的值,然后删除交换后的节点。
时间复杂度
各个操作,平均时间复杂度、最好时间复杂度为$O(log n)$,最坏情况时间复杂度为$O(n)$.
树的遍历
- 层序遍历 (Level order traversal) 从树的根开始从上到下,从左到右遍历。方法:通过队列把当前节点的子节点加入队列中实现。
- 深度优先遍历(DFS)
- 前序遍历 (pre-order) 先访问左子树,再处理当前节点,再处理右子树
- 中序遍历 (in-order) 先处理当前节点,再访问左子树,再处理右子树
- 后序遍历 (post-order) 先访问左子树,再处理右子树,再处理当前节点
B-树
一种平衡树。每个节点可以拥有m个元素,m+1个儿子节点。m=2时称为2-3树;m=3时称为2-3-4树。第k个子节点中的元素大于第k-1个元素,小于第k个元素。
操作
- find() 类似二叉搜索树。
- insert() 把新元素插入到已有节点中。若插入后节点元素多于限制数,(对于2-3-4树)把第二个元素作为该节点
- delete()
时间复杂度
查找、插入和删除操作的时间复杂度为稳定$O(log n)$量级。
红黑树
- 前置知识:通过旋转保持树的平衡。
- 左旋:将某个节点的右子树提升为新的根节点,并将原根节点变为其左子树。原根节点的右指针指向右孩子的左子树。
- 右旋:将某个节点的左子树提升为新的根节点,并将原根节点变为其右子树。原根节点的左指针指向左孩子的右子树。
性质(左倾红黑树)
- 与2-3树一一对应。
- 没有节点拥有2个红色边
- 没有红色向右的边
- 每条从树叶到根的路径中,黑色边的数量一致。
- 高度不高于对应2-3树的两倍
操作
- find() 类似二叉搜索树。
- insert() 把新元素插入到已有节点中。
- 插入时使用一条红色边。
- 当对应的2-3树出现“节点有第三个元素”时,左旋对应节点修复违反规则的链接。
- 当出现两个连续的黑色边时,右旋修复。
- 当出现一个节点连着两条左倾红色边时,让该节点连着的所有边颜色调转。 伪代码实现:
|
|
- delete()
时间复杂度
查找、插入和删除操作的时间复杂度为稳定$O(log n)$量级。(实现易于B-树,实际中更常用)
哈希表
把输入的元素通过哈希函数转化为整型数字后,将该数字作为数组下标存储到数组中。可以判断表中是否存在某元素。
存储的数据不需要具有可比较性
操作
add()把元素加入到哈希表中。contain()判断元素是否在哈希表中。
字符串to整型 存储
若存在n个可用字符,我们可以对这n个字符编号为1,2……n,t为字符编号。对于长度为m的字符串,我们可以使用公式 $N = \sum_{k=1}^m tn^{k-1}$ 计算字符对应的整形值,保证每个字符对应的整形值相同。
由于整型存在最大存储值上限2147483647,无法用整形表示所有字符串。
因此,不同字符串可能拥有相同的哈希值,导致哈希冲突。
Hash Codes
可以把计算得到的整型数字取余数减小哈希表大小。(余数为质数时更好避免哈希冲突)
java中object存在.hashcode()方法,为对象计算哈希值。我们同样也可以重写这个函数。
性质
- 必须为整型
- 对同一个对象多次调用必须返回相同值
- 两个对象只有当哈希值相同时才能被认为
.equals()
哈希冲突
链地址法
哈希表中每个元素都是一个链表,把哈希值相同的元素都加入到该链表中。(由于哈希表不允许重复元素,需要先判断元素是否存在于表中)。
时间复杂度(链地址法)
Q:元素链表中的元素个数
add()$Θ(Q)$ 使用动态数组后变为$Θ(1)$contain()$Θ(Q)$ 使用动态数组后变为$Θ(1)$
时间复杂度处理
为了防止链表过长降低时间复杂度
- 改进哈希函数使元素尽量均匀分布
- 动态增大数组大小(当 $M = 存储元素数/数组长度$ ) ,M大于一定值时创建大小为2倍的数组,并把旧的哈希表中的数据重新加入新的哈希表(可能导致存储位置的改变)。 改变数组大小的时间复杂度 $Θ(N)$。
堆与优先队列
类似于二叉搜索树,但是每次只能获取或者移除堆中最大/最小的元素。 下面以最小堆为例子。
性质
- 完全性: 只有最底层的节点有元素缺失,所有元素都尽可能存在于左边。
- 最小堆性质: 所有节点值都不大于其父节点
操作
public void add(Item x)在优先队列中加入元素public Item getSmallest()获取最小元素- `public Item getSmallest() 移除最小元素
public int size()返回优先队列大小
实现方法
使用数组keys[]存储优先队列。从下标1开始按序存储元素。可以得到leftChild(k)=k∗2 rightChild(k)=k∗2+1 parent(k)=k/2.
数组需要动态改变大小。
public void add(Item x)在优先队列中加入元素到堆的最后,然后让元素上浮直到满足堆性质。public Item getSmallest()返回堆的根节点元素public Item removeSmallest()把根节点元素的值变为堆的最后一个元素,删除堆的最后一个元素。把根节点元素下沉到满足性质的位置。
辅助函数public void swim(int k)
|
|
时间复杂度
| add(Item x) | getSmallest() | getSmallest() |
|---|---|---|
| $Θ(log N)$ | $Θ(1)$ | $Θ(log N)$ |
图(Graphs)
图:包括一系列节点,一系列连接两个节点的边。
简单图(Simple Graphs):任意两条边连接的节点不完全相同。一条边不能连接一个节点到它本身。
无向图(undirected graphs):边没有方向
有向图(directed graphs):边有方向
无环图(acyclic graphs): 图中没有环。即不存在能从一个节点出发,最终到达同一个节点的路径。
有环图(cyclic graphs):图中存在环。
图论问题
连通性问题
判断是否存在链接节点s,t的路径。使用深度优先遍历(DFS)。 用一个数组mark遍历过的节点。mark下当前节点后进入相邻的下一个节点。
|
|