1. 算法分析
2. 表、栈和队列
2.1 抽象数据类型
2.2 表ADT
- 前继和后继
- ArrayList和LinkedList特点:
- ArrayList查找和修改速度快,插入和删除速度慢,因其使用数组实现,对于插入/删除点后的元素需要重新给id赋值,但查找和修改可以根据下标直接操作。
- LinkedList插入和删除速度快,查找速度慢,因其用双向链表,插入/删除不只影响相邻点,但查找/修改需要从头/尾遍历。
3.3栈
- 先进后出
- 中缀转后缀:
- 操作数直接转输出
- 操作符使用栈处理
- 新操作符与栈顶元素比较,若优先级小于等于栈顶,则栈顶弹出。然后新操作入栈,否则新操作符直接压入。
- 对于”(“,直接入栈,只有在新操作符为“)”时,将其上所有元素弹出。
- 链表实现和数组实现
3.4 队列
- 先进先出
- 数组实现
- 用于服务器区块的响应,使用排队论解决响应与阻塞问题。
3. 树
- 大部分树拥有 O(logN) 的平均深度。
- 深度:从根到 nk 的最短路径,高度:从 nk 到树叶的最长路径
- 根的深度为0,树叶的高度为0。
- 从 n1 到 nk 的节点序列为 n1 到 nk 的路径。
- 三种遍历:
- 先序遍历:先父节点,后左,最后右
- 中序遍历:先左,后父节点,最后右
- 后序遍历:先左,后右,最后父节点
3.1 二叉树
3.2 ADT查找树
- 左子树每一个节点小于右子树的每一个节点
- 二叉树类总是支持comparable 的接口,使用compareTo来比较树内的每一项
- boolean contains(),BinaryNode findMin(),BinaryNode findMax()
- BinaryNode insert():查找,若不重复则插入到最后查找点的左/右子节点。
- BinaryNode remove():
- 若删除节点只有一个子节点,该子节点替代之
- 若有两个子节点,则寻找 右边最小项 替代之
- 对于有重复项的树,使用额外的空间(如表)记录节点频率
- 懒惰删除:仅标记为删除,节点频率减一,不实际从树中删去
3.3 AVL树
- 带有平衡条件的二叉查找树
- |H(leftTree)−H(rightTree)|≤1
- 除了可能的插入操作外,其他操作(查询,修改,删除)的时间复杂度都为 O(logN)
- 插入新点后再平衡的策略为:
- 从高度最大的不平衡子树的根节点开始:
- 若新插入点在左儿子的左子树或右儿子的右子树,使用单旋转
- 设不平衡子树的根节点为 k1 ,过深子树的儿子为 k2
- 将 k1 父节点作为 k2 父节点, k2 作为 k1 的父节点, k1 作为 k2 的左/右子节点
- 原 k2 的左/右节点成为 k1 的右/左节点
- 若新插入点在左儿子的右子树或右儿子的左子树,使用双旋转,双旋转等于两次单旋转,第一次对于不平衡子树的较深子树进行,第二次对于整个不平衡子树进行
- 设不平衡子树根节点为 k1 ,该子树的较深子树根节点为 k2 ,以 k2 作为根节点的子树的较深子树为 k3 。
- 先对较深子树( k2 )进行一次单旋转,完成旋转后 k3 为该子树的根节点
- 再对以 k1 为根节点, k3 为子节点的子树进行单旋转,结果 k3 成为根节点。
3.4 伸展树
- 保证M次操作时间: O(MlogN)
- 对于某一节点的访问将激活伸展操作
- 伸展操作的结果是该节点上升到根,收益为减少下次访问时间,同时可能减小树的深度
- 伸展操作分为“一字型”和“之字形”
- 设操作节点为 X,其父节点为P,P的父节点为G
- 一字型: X与P的左右关系和P与G的左右关系相同
- 之字型: X与P的左右关系和P与G的左右关系不相同
- 若为之字形,从 X开始向G做两次单旋转 ,否则从 G向X 做两次单旋转,最终结果相同, X成为根节点
- 删除时,可访问某一节点,将其升到根节点后删去,并取其左子树的最大叶节点替代
3.5 树的遍历
- 先序遍历:中左右
- 中序遍历:左中右
- 后序遍历:左右中
- 层序遍历:从上到下
3.5 B树
- B树是具有阶数M的不定叉树
- B树主要处理的目的是使得树在一定程度上更扁平化,因为判断分支所的计算花销要远小于读取数据指令,通过增加分叉树来减少读取数据的次数。
- 一个具有M阶的B树具有以下特性:
- 根节点必须是一个叶节点或拥有2到M个子节点
- 每个非叶非根节点必须拥有 [M2,M]个子节点
- 每个非叶节点使用M-1个关键字划分出M个子树
- 所有叶节点拥有同样的高度
- 每个叶节点包含一个数据块,包含 [L2,L]个数据
- 一个M阶B树的结构定性如下:
- 设每个磁盘区块容量为D字节,每个数据占用X个字节
- 对于每个节点含有M-1个关键值,占用 X∗(M−1)字节 ,同时分出M个子节点,使用4个字节记录每个子节点的磁盘地址。所以每个节点占用 X∗(M−1)+4M=(X+4)∗M−X个字节的地址 ,受限于区块容量限制,有 (X+4)∗M−X≤D ,由此解出M。
- 对于叶节点,同样受限于区块容量限制,有 L∗X≤D,求出L
- B树的插入和删除
- B树的插入操作:
- 简单的插入可能存在叶节点数据量大于 L 的问题。
- 当叶节点容量不足以加入新数据时,向上取父节点P,若P的关键数小于M-1,添加新的关键值进行分裂。否则,再继续向上尝试分裂父节点,直到成功为止。若到达根节点,则向上增加一层。
- 这是B树唯一的增高方式,也是根节点允许只拥有两个子树的原因。
- 另一种方式插入方式:
- 改变父节点的关键值,将合适的子树转入与父节点相邻的子树下。该方法使叶节点的数量区域饱和,适用于长时间运行的项目。
- B树的删除操作:
- 简单删除存在叶节点的数据量小于 L2 的问题
- 同样的,可以在删除后从父节点的相邻子树领养一个数据来解决,当相邻节点已达到最小值时 L2 ,则将该节点与相邻节点合并
- 节点合并可能会导致父节点的子树少于 M2 的情况,因此需要继续合并或领养。
- 当合并到达根节点的时候,不允许根节点仅有一个子节点,此时删除根节点,树高度减一。
- 这是唯一的减少树高度的方法。
3.6 Set和Map
- Set接口是一种不允许值重复的Collection, TreeSet是保证数值有序的Set的实现
- TreeSet提供自然排序和定制排序:
- 自然排序默认使用Object自带的Compare函数进行比较以确定是否重复。
- 定制排序在实例化时传入Camparetor实例来定义比较结果,0为相同
- TreeSet由于数据的顺序性,提供first(),last(), subset(Obj, Obj), headset(Obj), tailSet(Obj), higher(Obj), lower(Obj)方法。
- Map接口是由键值对构成的Collection,其中键是不可重复的,但是值可以,TreeMap是SortedMap的实现,其中的键是有序排列的。
4. 散列
5. 优先队列(堆)
5.1 堆概述
- 堆的作用是对最小元素进行常数时间的读取
- 堆一般主要用于顺序管理
- 堆的两性:
- 堆序性质:父节点恒小于等于左右子节点
- 结构性质:总是完全二叉树,叶节点从左到右填满
5.2 二叉堆:
- 二叉堆可以使用数组实现:任意节点 i 的子节点位于 2i 和 2i+1 上
- 二叉堆的插入/上滤(最坏O(logN)):
- 先插入末尾空位
- 检查是否符合堆序性质
- 若不符合则与父节点换位
- 重复2,3直到符合堆序性质
- 二叉堆的堆顶删除/下滤:
- 先删除堆顶节点
- 将末尾节点填入堆顶
- 检查是否符合堆序性质
- 若不符合,和较小的子节点换位
- 重复3,4直到满足堆序性质
5.3 左式堆
- 左式堆满足堆的堆序性质
- 左式堆的节点除了记录根节点,左右子节点外,还需要记录零路径长。
- 左式堆的结构性质:
- 零路径长npl(null path length):到最近不含双子节点的节点的距离
- 左式堆的任意左子节点的零路径长大于等于兄弟右节点的零路径长
- 左式堆的优点:
- 左式堆的合并排序
- 合并操作使用递归进行
- 将 根节点较大的子树 H1 与 根节点较小子树的右子树 H2 合并,返回值作为 H2 的新右子树
- 若其中一个子树为空,则返回另一子树
- 若 H2 左子树为空,则将 H1 作为 H2 的左子树,并返回
- 在迭代返回中,检测每个左右节点的npl,若不满足左式堆性质,则交换左右子树,并更新npl
排序
不相交集类
图论算法
算法设计技巧