二叉排序树
左子树中所有结点的值,均小于其根结点的值。
右子树中所有结点的值,均大于其根结点的值。
二叉搜索树的子树也是二叉搜索树。
注意:
1.二叉查找树不能插入重复元素
2.中序遍历是一个递增的数列
3.高度越小查询效率越高
二叉排序树的删除操作
要删除的结点是叶子结点。
要删除的结点是只有一个孩子结点。
要删除的结点有两个孩子结点。
第一种情况,直接删除就行了
第二种情况,需要将孩子结点连接上去
第三种情况有两个方案:推荐方案1
1. 选取其左子树中最大结点上位
2. 选择其右子树中最小结点上位
平衡二叉树
为弥补排序树因不平衡而导致查询效率下降的情况,从而推出平衡二叉树(AVL树)
性质
平衡二叉树一定是一棵二叉排序树。
任意结点的左右子树也是一棵平衡二叉树。
从根节点开始,左右子树都高度差(平衡因子)不能超过1,否则视为不平衡
平衡因子:二叉树上节点的左子树高度减去右子树高度
插入操作
当插入之后,不再满足平衡二叉树的定义时,就出现了失衡的情况,而对于这种失衡情况,为了继续保持平衡状态,就需要进行处理
根据插入结点的不同偏向情况,分为LL型、LR型、RR型、RL型
LL型调整(右旋)
RR型调整(左旋)
RL型调整(先右旋,再左旋)
LR型调整(先左旋,再右旋)
删除操作类似,在删除之后判断是否平衡,如果不平衡同样需要进行旋转操作
红黑树
二叉平衡树,通过在插入结点时维护树的平衡,这样就不会出现极端情况使得整棵树的查找效率急剧降低了。因为一旦平衡因子的绝对值超过1那么就失衡,这样每插入一个结点,就有很大的概率会导致失衡,能否不这么严格,但同时也要在一定程度上保证平衡呢?这就要提到红黑树了
它并不像平衡二叉树那样严格要求高度差不能超过1,但需要满足五个规则
规则1:每个结点可以是黑色或是红色。
规则2:根结点一定是黑色。
规则3:红色结点的父结点和子结点不能为红色,也就是说不能有两个连续的红色。
规则4:所有的空结点都是黑色(空结点视为NIL,红黑树中是将空节点视为叶子结点)
规则5:每个结点到空节点路径上出现的黑色结点的个数都相等。
插入节点
通过变色和旋转来保持原有的性质
变色:直接将父结点和其兄弟结点同时修改为黑色(兄弟结点变成黑色,因为要满足性质5)然后将爷爷结点改成红色,当爷爷结点为根结点时再变回黑色
如果整棵树为NULL,直接作为根结点,变成黑色。
如果父结点是黑色,直接插入就完事。
如果父结点为红色,且父结点的兄弟结点也是红色,直接变色即可
如果父结点为红色,但父结点的兄弟结点为黑色,需要先根据情况(LL、RR、LR、RL)进行旋转,然后再变色。
B树
B树(Balance Tree),是专门为磁盘数据读取设计的一种度为 m 的查找树(多用于数据库)它同样是一棵平衡树,但是不限于二叉,前面的二叉树都是基于内存读取的优化,这个是磁盘读取的优化,一棵度为4的(4阶)B树
规则
树中每个结点最多含有m个孩子(m >= 2)比如上面就是m为4的4阶B树,最多有4个孩子。
除根结点和叶子结点外,其它每个结点至少有⌈m/2⌉个孩子,同理键值数量至少有⌈m/2⌉-1个。
若根结点不是叶子结点,则至少有2个孩子。
所有叶子结点都出现在同一层。
一个结点的包含多种信息(P0,K1,P1,K2,…,Kn,Pn),其中P为指向子树的指针,K为键值(关键字)
Ki (i=1...n)为键值,也就是每个结点保存的值,且键值按顺序升序排序K(i-1)< Ki
Pi为指向子树的指针,且指针Pi指向的子树中所有结点的键值均小于Ki,但都大于K(i-1)
除根结点外其他所有的结点的键值个数n必须满足: ⌈m/2⌉-1 <= n <= m-1
插入操作
如果该节点上的元素数未满,则将新元素插入到该节点,并保持节点中元素的顺序。
如果该节点上的元素已满,则需要将该节点平均地分裂成两个节点:
首先从该节点中的所有元素和新元素中先出一个中位数作为分割值。
小于中位数的元素作为左子树划分出去,大于中位数的元素作为右子树划分。
分割值此时上升到父结点中,如果没有父结点,那么就创建一个新的父节点
因为要满足B树第四条规则:所有叶子结点都出现在同一层,当叶子结点饱满时要向上划分
删除操作
若删除的是叶子结点的中元素:
正常情况下直接删除。
如果删除后,键值数小于最小值,那么需要找兄弟借一个。
要是没得借了,直接跟兄弟结点、对应的分割值合并。
若删除的是某个根结点中的元素:
一般情况会删掉一个分割值,删掉后需要重新从左右子树中找一个新分割值的拿上来。
要是拿上来之后左右子树中出现键值数小于最小值的情况,那么就只能合并了。
上述两个操作执行完后,还要继续往上看上面的结点是否依然满足性质,否则继续处理,直到稳定
与红黑树的关系
红黑树 和 4阶B树具有等价性,其中黑色结点就是中间的(黑色结点一定是父结点),红色结点分别位于两边,通过将黑色结点与它的红色子节点融合在一起,形成1个B树节点,
B树叶节点等深实际上体现在红黑树中为任一叶节点到达根节点的路径中,黑色路径所占的长度是相等的,因为黑色结点就是B树的结点分割值。
B树节点的键值数量不能超过N实际上体现在红黑树约定相邻红色结点接最多2条,也就是说不可能出现B树中元素超过3的情况,因此是4阶B树。
B+树
B+树是B树的一种变体,有着比B树更高的查询性能
有k个子树的中间结点包含有k个元素(B树中是k-1个元素),每个元素不保存数据,只用来索引,所有数据都保存在叶子结点。
所有的叶子结点中包含了全部元素的信息,及指向含这些元素记录的指针,且叶子结点按照从小到大的顺序连接。
所有的根结点元素都同时存在于子结点中,在子节点元素中是最大(或最小)元素。
比较:
B树的查找性能并不稳定,最好的情况是只查根节点即可,而最坏的情况则需要查到叶子节点,B+树每一次查找都是稳定的,因为一定在叶子结点
应用
MySQL就默认选择B+Tree作为索引的存储数据结构
MyISAM存储引擎下的B+Tree实现
InnoDB存储引擎下的B+Tree实现
哈夫曼树
给定N个权值作为N个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树
选择两棵权值最小的树作为一颗新的树的左右子树,左右顺序不重要(因为哈夫曼编码不唯一),得到的树根结点权值为这两个结点之和,再将这颗子树放回森林,重复上面的操作,继续选择两个最小的出来组成一颗新的树
定理:对于具有n个叶子结点的哈夫曼树,一共有2n-1个结点
哈夫曼编码
然后就可以编码了
优先级队列
与普通队列不同,它允许插队(权值越大的元素优先排到前面去),出队还是一律从队首出来
堆
必须是一棵完全二叉树,树中父亲都比孩子小的我们称为小根堆(小顶堆),树中父亲都比孩子大则是大根堆(注意不要跟二叉查找树搞混了,二叉查找树是左小右大,而堆只要是孩子一定小或者大),它是一颗具有特殊性质的完全二叉树,同样可以实现优先级队列
因为完全二叉树比较适合使用数组才存储(因为是按序的)所以说一般堆都是以数组形式存放,并且第一个位置是空的
大顶堆的插入和删除操作
插入:
因为是一棵完全二叉树,那么必须按照顺序,继续在最后一行从左往右插入新的结点,其实就相当于在数组的后面继续加一个新的结点进来,破坏规则时只需和父节点交换即可,并需要持续向上比较和交换,直到稳定为止
删除队首元素
删除最顶上的元素,此时先把排在最后面的拿上来顶替一下,按照与插入相反的方向,从上往下进行堆化操作,规则是一样的,遇到大的就交换,即使完成了出队操作,依然是最大的元素排在队首,并且整棵树依然是一棵完全二叉树