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 10
public 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下当前节点后进入相邻的下一个节点。
|
|