一、AVL树
1.每个结点的平衡因子的绝对值不超过1(某个节点x的平衡因子=左孩子高度-右孩子高度)
2.高度看的是这棵树中最深的那个节点,深度看的是这个节点的层次(规定根节点的那一层为第0层)
3.插入和删除的区别
失衡节点 | 需要进行的操作 | 进行操作后树的总高度是否变化 | 进行一次调整后失衡会不会向上传播 | |
---|---|---|---|---|
插入 | 可能有多个 | 单旋或者双旋 | 旋转后高度会恢复到未插入节点前 | 不会 |
删除 | 只会有1个,甚至没有 | 单旋或者双旋 | 恢复平衡后高度可能比删除节点的要小 | 会 |
4.重平衡根据g(x)找p,v节点时,都是优先选择高度更高的孩子或者和父亲同侧的孩子(这是俩孩子等高的情况下采取的措施)
5.删除节点时什么时候会导致树高比删除前的要小?
6.按递增次序将2h+1-1个关键码插入初始为空的AVL树中,必然得到高度为h的满树(习题7-20)。然后利用这个结论,结合二叉查找树左小右大的特点,可以很容易地证明下面这个题目的充分性;至于必要性,可以比较直观地看出来,如果不是满树,那两次构造的堆肯定不一样
至于具体的证明,请看大佬的github
二、伸展树(splay树,P205)
1)经前人证明,伸展树的单次操作均可在分摊的O(logn)时间内完成(证明过程你不要学,记住结论就行)
2)在伸展树中,如果,即便节点v的深度为Ω(n),双层伸展策略既可将v推至树根,还可以让对应分支的长度收缩到原来的一半
3)删除节点的算法思想:
主要是要记一下,删除节点后,新的伸展树的树根为TR中的最小关键码
概括一下,伸展树中一个节点的删除包括:一次查找+一轮伸展+一次摘除+又一次查找+又一轮伸展
4)插入节点的算法如下,这个就没什么好说的了,就是:一次查找+一轮伸展
5)AVL树中也有单旋和双旋,和splay树中的单层伸展和双层伸展有点类似,但是splay树是要把刚访问过的节点挪到树根,一次插入或者删除都需要多次伸展;而AVL树则是把不平衡性解决了就行,除了少数情况下的删除,其它大部分情况都只需要一次单旋或者一次双旋;从这里也可以看出来,AVL树关注的是平衡性,splay树关注的被访问的那个节点
6)伸展树和AVL树的比较:
编程难易程度 | 分摊复杂度 | 局部性 | |
---|---|---|---|
AVL | 需要记录结点高度和平衡因子,较为复杂 | 两种树相同,都是O(logn) | 不适用于局部性很强的场合 |
splay | 不需要记录结点高度和平衡因子,相对简单 | 两种树相同,都是O(logn) | 适用于局部性强的场合 |
其它方面的评价:
i)不能杜绝单次最坏情况出现,不适用于对效率敏感的场合
ii)复杂度的分析稍显复杂
三、B-树
1)对于m阶B树,相较于同等结点规模的BBST,最大树高会缩减到1/(log2m-1),最小树高会缩减到1/log2m。所以,对于256阶B树,至少可以缩减到原二分查找树的1/7,最多可以缩减到1/8
四、红黑树
五、左式堆(用于堆合并,P297)
1.最右侧通路和堆的最小规模的关系如果最右侧通路的长度为d,那么至少包含2d+1-1个节点(包括了外部节点),至少包含2d-1个内部节点
2.npl(null path length)和高度、深度之类的不一样,它的定义如下:
而左式堆中又要保证所有节点的npl(x->lchild)
≥npl(x->rchild)
这样一来,npl(x)=1+npl(x->rchild)
,即左式堆中某个节点的npl只取决于其右孩子
3.左倾并不意味着左孩子的高度必大于等于右孩子的高度,左倾只能说明每个节点到达右孩子的空节点不比左孩子的空节点慢。如下面这个左式堆,就是右孩子高度大于左孩子高度:
所以,也说明左孩子的规模并不一定大于等于右孩子的规模,
即左孩子的高度和规模都可以小于右孩子。
4.每个节点的npl值,恰好等于其最右侧通路的长度
5.最右侧通路:从x出发沿右侧分支一直前行直至空节点,经过的通路称作其最右侧通路,记作rPath(x)
6.根节点的最右侧通路的终点必为全堆中深度最小的外部节点
7.合并:
结合实例更直观一点:
每次迭代开始时,都先判断两个堆中,谁要当谁的孩子,以及谁要移到左边,谁要移到右边:比如17要放在左边,15要放在右边,于是就把17的右孩子拿出来和以15为根的堆来进行合并成17的新右孩子;当合并全部完成后,还要从当前合并堆的父节点出发,考察它左右孩子的npl值,看是否需要调换左右孩子的顺序,比如上面这个实例中的d->e
8.借助左式堆进行堆合并的时间复杂度为O(max(logn,logm))
,堆合并的算法如下,可以看到,通过第5行的swap,,确保右边的堆的根都是较小的那个:
9.