平衡二叉树

各种树型结构的大致实际应用场景:

AVL树:平衡二叉树之一,应用相对其他数据结构比较少,windows对进程地址空间的管理用到了AVL

红黑树:平衡二叉树,广泛应用在C++STL中,比如map和set,Java的TreeMap

B和B+树:主要用在文件系统以及数据库中做索引等

Trie 树: 字符匹配, IP 选路

动态查找树

动态查找树主要有二叉查找树(Binary Search Tree),平衡二叉查找树(Balanced Binary Search Tree), 红黑树 (Red-Black Tree ),

都是典型的二叉查找树结构,查找的时间复杂度 O(log2-N) 与树的深度相关,降低树的深度会提高查找效率,于是有了多路的B-tree/B+-tree/ B*-tree (B~Tree)。

4.1 二叉查找树

参考: https://www.jianshu.com/p/3257d0a4fdf2

4.2 AVL 树

高度平衡的二叉树
通常,维护这种高度平衡所付出的代价比从中获得的效率收益还大;实际用不多;
更多的地方是用追求局部而不是非常严格整体平衡的红黑树。
当然,如果场景中对插入删除不频繁,只是对查找特别有要求,AVL还是优于红黑的。
使用场景:Windows对进程地址空间的管理用到了AVL树。

定义

平衡因子(Balance Tree, 简称BF): ()=ℎ−ℎ,其中ℎ和ℎ分别为T的左右子树的高度。

平衡二叉树(Balanced Binary Tree, 或称AVL树):

空树,或者任意结点的左右子树高度差的绝对值不超过1,即|()|≤1。


平衡二叉树.png

1.给定节点数为 n 的 AVL 树的最大高度为 O(log_2n)
n_h是高度为 h的平衡二叉树的最小节点;

h n_h f_h
0 1 1
1 2 1
2 4 2
3 7 3
4 12 5
5 20 8
6 33 13
7 54 21
8 88 34

n_h = n_{h-1} +n_{h-2} +1
而 斐波那契序列:
F_0= 1, F_1=1, F_i = F_{i-1} + F_{i-2} , for \ i>1
i很大时,
F_i = \frac{1}{\sqrt 5}(\frac{1+\sqrt{5}}{2})^i
故而,
n_h = F_{h+2} -1 ,for\ h\ge0
\Rightarrow n_h\approx \frac{1}{\sqrt 5}(\frac{1+\sqrt{5}}{2})^{h-2} -1
\Rightarrow O(N) = log_2(N)

结论:给定结点数为N的AVL树的最大高度为 (2)

AVL的调整

右单旋(RR旋转)


不平衡的“发现者”是Mar,“麻烦结点”是Nov,在发现者右子树的右边,因而叫RR插入,此时Mar 的平衡因子BF=-2,为保持平衡,需要RR旋转。

如图所示,基本思想是: 把B的左子树腾出来挂到A的右子树上,返回B作为当前子树的根

左单旋(LL旋转)


不平衡的“发现者”是Mar,“麻烦结点”是Apr,在发现者左子树的左边,因而叫LL插入,此时Mar和May 的平衡因子BF=2,为保持平衡,需要LL旋转.

如图所示,基本思想是: 把B的右子树腾出来挂到A的左子树上,返回B作为当前子树的根

左-右单旋(LR旋转)

不平衡的“发现者”是May,“麻烦结点”是Jan,在发现者左子树的右边,因而叫LR插入,需要LR旋转,即把Aug的右子树Mar,调整为根结点。


如图所示,基本思想是:先将B作为根结点进行RR单旋转化为LL插入,再将A作为根结点进行LL单旋(先 RR 再 LL)

右-左单旋(RL旋转)


不平衡的“发现者”是Aug,“麻烦结点”是Feb,在发现者右子树的左边,因而叫RL插入,需要RL旋转,即调整Jan的左子树Dec为Aug和Jan根结点.


如图所示,基本思想是:先将B作为根结点进行LL单旋转化为RR插入,再将A作为根结点进行RR单旋(先 LL 再 RR)

程序实现


应用:是否为同一颗二叉搜索树

题意理解

给定一个插入序列就可以唯一确定一棵二叉搜索树,然而一棵给定的二叉搜索树却可以由多种不同的插入序列得到。eg.序列{2,1,3}和{2,3,1}可以得到一样的结果。

那么:对于输入的各种插入序列,你是否可以判断他们是否能生成一样的二叉搜索树。

输入样例

4 2
3 1 4 2
3 4 1 2
3 2 4 1

第1行代表序列长度为4,且有2个待比较序列;第2行为序列原型,3,4行为具体的待比较序列。

输出样例

Yes
No

求解思路

1、分别建两棵搜索树

2、不建树

3、建一棵树,再判别其他序列是否与该树一致

  • 搜索树表示;
  • 建搜索树T;
  • 判别一序列是否与搜索树T一致

程序框架

int main()
{
    对每组数据:
    1、读入N和L;
    2、根据第一行序列建树T
    3、根据树T分别判别后面
    的L个序列是否能与T形成
    同一搜索树并输出结果
    return 0;
}

程序实现

4.2 红黑树

近似平衡的平衡二叉树;

通过对任何一条从根到叶子的简单路径上各个节点的颜色进行约束,确保没有一条路径会比其他路径长2倍近似平衡的。

所以相对于严格要求平衡的AVL树来说,它的旋转保持平衡次数较少。用于搜索时,插入删除次数多的情况下我们就用红黑树来取代AVL。

使用场景:

  • 广泛用在C++的STL中。map和set都是用红黑树实现的
  • 著名的linux进程调度Completely Fair Scheduler,用红黑树管理
  • 进程控制块
  • epoll在内核中的实现,用红黑树管理事件块
  • nginx中,用红黑树管理timer等
  • Java的TreeMap实现

4.3 B树 B+树

B树,B+树特点是一样的,是多路查找树,一般用于数据库系统中;

B树,B+树分支多层数少;使用时螚有效的减少磁盘IO次数避免磁盘频繁的查找;

使用场景:

  • 主要用在文件系统以及数据库中做索引等,比如Mysql:B-Tree Index in MySql;
  • 像mysql的数据库定义是可以指定B+ 索引还是hash索引;
B树(也叫 B-树)

B-树是为了磁盘或其它存储设备而设计的一种多叉平衡查找树。

(1) B树结构

B-tree中,每个结点包含:
本结点所含关键字的个数;
指向父结点的指针;
关键字;
指向子结点的指针数组;

 #define MAX 1000                   //结点中关键字的最大数目:Max=m-1,m是B-树的阶
 #define MIN 500                    //非根结点中关键字的最小数目:Min=m/2-1
 typedef int KeyType;               //KeyType关键字类型由用户定义
 typedef struct BTreeNode *BTree;
 typedef struct BTreeNode{          //结点定义中省略了指向关键字代表的记录的指针
     int keyNum;                    //结点中当前拥有的关键字的个数,keynum<

(2)B-tree的特点

B-tree是一种多路搜索树(并不是二叉的),对于一棵M阶树:

  • 定义任意非叶子结点最多只有M个孩子;且M>2;

  • 根结点的孩子数为[2, M],除非根结点为叶子节点;

  • 除根结点以外的非叶子结点的儿子数为[M/2, M];

  • 非叶子结点的关键字个数=指向儿子的指针个数-1;

  • 每个非叶子结点存放至少M/2-1(取上整)和至多M-1个关键字;

  • 非叶子结点的关键字:K[1], K[2], …, K[M-1];且K[i] < K[i+1];

  • 非叶子结点的指针:P[1], P[2], …, P[M];其中P[1]指向关键字小于K[1]的子树,P[M]指向关键字大于K[M-1]的子树,其它P[i]指向关键字属于(K[i-1], K[i])的子树;

  • 所有叶子结点位于同一层;

以M=3的一棵3阶B树为例:

3 阶B树

(3)B-tree高度与复杂度

B树的高度是
H \leqslant log_T \frac{(n+1)}{2}

而不是其它几种树的 H=log_2n其中:

T为度数(每个节点包含的元素个数),即所谓的阶数;
n为总元素个数或总关键字数。

B树查找的时间复杂度为O(Log_2N),下面是参考推导过程:

其中M为设定的非叶子结点最多子树个数,N为关键字总数;所以B-树的性能总是等价于二分查找(与M值无关),也就没有AVL树平衡的问题。

(3)B树的基本操作

(1)查找操作

在B-树中查找给定关键字的方法类似于二叉排序树上的查找。不同的是在每个结点上确定向下查找的路径不一定是二路而是keynum+1路的。
对结点内的存放有序关键字序列的向量key[l..keynum] 用顺序查找或折半查找方法查找。若在某结点内找到待查的关键字K,则返回该结点的地址及K在key[1..keynum]中的位置;否则,确定K在某个key[i]和key[i+1]之间结点后,从磁盘中读son[i]所指的结点继续查找。直到在某结点中查找成功;或直至找到叶结点且叶结点中的查找仍不成功时,查找过程失败。

(2)查找操作的时间开销

B-树上的查找有两个基本步骤:
1.在B-树中查找结点,该查找涉及读盘DiskRead操作,属外查找;
2.在结点内查找,该查找属内查找。
查找操作的时间为:
1.外查找的读盘次数不超过树高h,故其时间是O(h);
2.内查找中,每个结点内的关键字数目keynum 注意:
1.实际上外查找时间可能远远大于内查找时间。
2.B-树作为数据库文件时,打开文件之后就必须将根结点读人内存,而直至文件关闭之前,此根一直驻留在内存中,故查找时可以不计读入根结点的时间。

(3)插入操作

插入一个元素时,首先在B树中是否存在,如果不存在,即在叶子结点处结束,然后在叶子结点中插入该新的元素,注意:如果叶子结点空间足够,这里需要向右移动该叶子结点中大于新插入关键字的元素,如果空间满了以致没有足够的空间去添加新的元素,则将该结点进行“分裂”,将一半数量的关键字元素分裂到新的其相邻右结点中,中间关键字元素上移到父结点中(当然,如果父结点空间满了,也同样需要“分裂”操作),而且当结点中关键元素向右移动了,相关的指针也需要向右移。如果在根结点插入新元素,空间满了,则进行分裂操作,这样原来的根结点中的中间关键字元素向上移动到新的根结点中,因此导致树的高度增加一层。

(4)删除操作

首先查找B树中需删除的元素,如果该元素在B树中存在,则将该元素在其结点中进行删除,如果删除该元素后,首先判断该元素是否有左右孩子结点,如果有,则上移孩子结点中的某相近元素到父节点中,然后是移动之后的情况;如果没有,直接删除后,移动之后的情况。

B^+

(1)B树和B+树的对比

一棵m阶的B+树和m阶的B树的异同点在于:

1.有n棵子树的结点中含有n-1 个关键字;

2.所有的叶子结点中包含了全部关键字的信息,及指向含有这些关键字记录的指针,且叶子结点本身依关键字的大小自小而大的顺序链接。 (而B 树的叶子节点并没有包括全部需要查找的信息)

3.所有的非终端结点可以看成是索引部分,结点中仅含有其子树根结点中最大(或最小)关键字。 (而B 树的非终节点也包含需要查找的有效信息)
下面是一棵典型的 B+ 树:


(2)为什么说B+树比B 树更适合实际应用中操作系统的文件索引和数据库索引?

(1) B+树的磁盘读写代价更低;

B+树的内部结点并没有指向关键字具体信息的指针。因此其内部结点相对B 树更小;

如果把所有同一内部结点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多。

一次性读入内存中的需要查找的关键字也就越多。相对来说IO读写次数也就降低了。

举个例子,假设磁盘中的一个盘块容纳16 bytes,而一个关键字2 bytes,一个关键字具体信息指针2 bytes。

一棵9阶B-tree(一个结点最多8个关键字)的内部结点需要2个盘快。而B+ 树内部结点只需要1个盘快。当需要把内部结点读入内存中的时候,B 树就比B+ 树多一次盘块查找时间(在磁盘中就是盘片旋转的时间)。

(2) B+树查询效率更加稳定

由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。

B^*

B*-tree是B+-tree的变体,在B+树的基础上(所有的叶子结点中包含了全部关键字的信息,及指向含有这些关键字记录的指针),

B*树中非根和非叶子结点再增加指向兄弟的指针;

B树定义了非叶子结点关键字个数至少为(2/3)M,即块的最低使用率为2/3(代替B+树的1/2)。

下图是一棵典型的B*树:


4.4 Trie树

又名单词查找树,一种树形结构;常用来操作字符串;

它是不同字符串的相同前缀只保存一份。相对直接保存字符串肯定是节省空间的,但是它保存大量字符串时会很耗费内存。

类似的有
前缀树(prefix tree),后缀树(suffix tree),radix tree(patricia tree, compact prefix tree),crit-bit tree(解决耗费内存问题,double array trie。

使用场景:

  • 字符匹配
    前缀树:字符串快速检索,字符串排序,最长公共前缀,自动匹配前缀显示后缀;
    后缀树:查找字符串s1在s2中,字符串s1在s2中出现的次数,字符串s1,s2最长公共部分,最长回文串;
    radix tree:linux内核,nginx;

  • IP选路
    也是前缀匹配,一定程度会用到trie


参考资料:
https://www.zhihu.com/question/30527705
https://www.cnblogs.com/binyue/p/5072264.html
https://www.cnblogs.com/tgycoder/p/5077017.html

你可能感兴趣的:(平衡二叉树)