概念算法
二叉查找树又称二叉搜索树,二叉排序树,特色以下:数据库
任何一个数据的查找过程都须要从根结点出发,沿某一个路径朝叶子结点前进。所以查找中数据比较次数与树的形态密切相关。 数组
当树中每一个结点左右子树高度大体相同时,树高为logN。则平均查找长度与logN成正比,查找的平均时间复杂度在O(logN)数量级上。 性能
当前后插入的关键字有序时,BST退化成单支树结构。此时树高n。平均查找长度为(n+1)/2,查找的平均时间复杂度在O(N)数量级上。优化
新结点插入到树的叶子上,彻底不须要改变树中原有结点的组织结构。插入一个结点的代价与查找一个不存在的数据的代价彻底相同。指针
当删除一个结点P,首先须要定位到这个结点P,这个过程须要一个查找的代价。而后稍微改变一下树的形态。若是被删除结点的左、右子树只有一个存在,则改变形态的代价仅为O(1)。若是被删除结点的左、右子树均存在,只须要将当P的左孩子的右孩子的右孩子的…的右叶子结点与P互换,在改变一些左右子树便可。所以删除操做的时间复杂度最大不会超过O(logN)。排序
查找最好时间复杂度O(logN),最坏时间复杂度O(N)。 索引
插入删除操做算法简单,时间复杂度与查找差很少。内存
二叉查找树在最差状况下居然和顺序查找效率至关,这是没法仍受的。事实也证实,当存储数据足够大的时候,树的结构对某些关键字的查找效率影响很大。固然,形成这种状况的主要缘由就是BST不够平衡(左右子树高度差太大)。既然如此,那么咱们就须要经过必定的算法,将不平衡树改变成平衡树。所以,AVL树就诞生了。效率
AVL是严格平衡的BST(平衡因子不超过1)。那么查找过程与BST同样,只是AVL不会出现最差状况的BST(单支树)。所以查找效率最好,最坏状况都是O(logN)数量级的。
AVL必需要保证严格平衡(|bf|<=1),那么每一次插入数据使得AVL中某些结点的平衡因子超过1就必须进行旋转操做。事实上,AVL的每一次插入结点操做最多只须要旋转1次(单旋转或双旋转)。所以,整体上插入操做的代价仍然在O(logN)级别上(插入结点须要首先查找插入的位置)。
AVL删除结点的算法能够参见BST的删除结点,可是删除以后必须检查从删除结点开始到根结点路径上的全部结点的平衡因子。所以删除的代价稍微要大一些。每一次删除操做最多须要O(logN)次旋转。所以,删除操做的时间复杂度为O(logN)+O(logN)=O(2logN)
查找的时间复杂度维持在O(logN),不会出现最差状况
AVL树在执行每一个插入操做时最多须要1次旋转,其时间复杂度在O(logN)左右。
AVL树在执行删除时代价稍大,执行每一个删除操做的时间复杂度须要O(2logN)。
二叉平衡树的严格平衡策略以牺牲创建查找结构(插入,删除操做)的代价,换来了稳定的O(logN) 的查找时间复杂度。可是这样作是否值得呢?
能不能找一种折中策略,即不牺牲太大的创建查找结构的代价,也能保证稳定高效的查找效率呢? 答案就是:红黑树。
因为红黑树的性质(最长路径长度不超过最短路径长度的2倍),能够说明红黑树虽然不像AVL同样是严格平衡的,但平衡性能仍是要比BST要好。其查找代价基本维持在O(logN)左右,但在最差状况下(最长路径是最短路径的2倍少1),比AVL要略逊色一点。
RBT插入结点时,须要旋转操做和变色操做。但因为只须要保证RBT基本平衡就能够了。所以插入结点最多只须要2次旋转,这一点和AVL的插入操做同样。虽然变色操做须要O(logN),可是变色操做十分简单,代价很小。
RBT的删除操做代价要比AVL要好的多,删除一个结点最多只须要3次旋转操做。
查找 效率最好状况下时间复杂度为O(logN),但在最坏状况下比AVL要差一些,但也远远好于BST。
插入和删除操做改变树的平衡性的几率要远远小于AVL(RBT不是高度平衡的)。所以须要的旋转操做的可能性要小,并且一旦须要旋转,插入一个结点最多只须要旋转2次,删除最多只须要旋转3次(小于AVL的删除操做所须要的旋转次数)。虽然变色操做的时间复杂度在O(logN),可是实际上,这种操做因为简单所须要的代价很小。
对于在内存中的查找结构而言,红黑树的效率已经很是好了(实际上不少实际应用还对RBT进行了优化)。可是若是是数据量很是大的查找呢?将这些数据所有放入内存组织成RBT结构显然是不实际的。实际上,像OS中的文件目录存储,数据库中的文件索引结构的存储…. 都不可能在内存中创建查找结构。必须在磁盘中创建好这个结构。那么在这个背景下,RBT仍是一种好的选择吗?
在磁盘中组织查找结构,从任何一个结点指向其余结点都有可能读取一次磁盘数据,再将数据写入内存进行比较。你们都知道,频繁的磁盘IO操做,效率是很低下的(机械运动比电子运动要慢不知道多少)。显而易见,全部的二叉树的查找结构在磁盘中都是低效的。所以,B树很好的解决了这一个问题。
B-Tree做为一个平衡多路查找树(m-叉)。B树的查找分红两种:一种是从一个结点查找另外一结点的地址的时候,须要定位磁盘地址(查找地址),查找代价极高。另外一种是将结点中的有序关键字序列放入内存,进行优化查找(能够用折半),相比查找代价极低。而B树的高度很小,所以在这一背景下,B树比任何二叉结构查找树的效率都要高不少。并且B+树做为B树的变种,其查找效率更高。
B-Tree的插入会发生结点的分裂操做。当插入操做引发了s个节点的分裂时,磁盘访问的次数为h(读取搜索路径上的节点)+2s(回写两个分裂出的新节点)+1(回写新的根节点或插入后没有致使分裂的节点)。所以,所须要的磁盘访问次数是h+2s+1,最多可达到3h+1。所以插入的代价是很大的。
B-Tree的删除会发生结点合并操做。最坏状况下磁盘访问次数是3h=(找到包含被删除元素须要h次读访问)+(获取第2至h层的最相邻兄弟须要h-1次读访问)+(在第3至h层的合并须要h-2次写访问)+(对修改过的根节点和第2层的两个节点进行3次写访问)。
一颗m阶(m>=3,即一个结点包含的数据和子节点数),3阶B-树有以下特色:
1. 根结点之多3颗子树
2. 定义:
define m 3 /*B 树的阶*/
typedef struct Node{
int keynum; /* 结点中关键码的个数,即结点的大小*/
int key[m]; /*结点数据数组*/
struct Node *parent; /*指向父节点的指针*/
Node*son[m]; /*指向子结点的指针数组*/
};
因为考虑磁盘储存结构,B树的查找、删除、插入的代价都远远要小于任何二叉结构树(读写磁盘次数的下降)。
AVL 和RBT 都是二叉查找树的优化。其性能要远远好于二叉查找树。他们之间都有本身的优点,其应用上也有不一样。
结构对比: AVL的结构高度平衡,RBT的结构基本平衡。平衡度AVL > RBT.
查找对比: AVL 查找时间复杂度最好,最坏状况都是O(logN)。RBT 查找时间复杂度最好为O(logN),最坏状况下比AVL略差。
插入删除对比:
1. AVL的插入和删除结点很容易形成树结构的不平衡,而RBT的平衡度要求较低。所以在大量数据插入的状况下,RBT须要经过旋转变色操做来从新达到平衡的频度要小于AVL。
2. 若是须要平衡处理时,RBT比AVL多一种变色操做,并且变色的时间复杂度在O(logN)数量级上。可是因为操做简单,因此在实践中这种变色仍然是很是快速的。
3. 当插入一个结点都引发了树的不平衡,AVL和RBT都最多须要2次旋转操做。但删除一个结点引发不平衡后,AVL最多须要logN 次旋转操做,而RBT最多只须要3次。所以二者插入一个结点的代价差很少,但删除一个结点的代价RBT要低一些。
4. AVL和RBT的插入删除代价主要仍是消耗在查找待操做的结点上。所以时间复杂度基本上都是与O(logN) 成正比的。
整体评价:大量数据实践证实,RBT的整体统计性能要好于平衡二叉树。
B+树是B-树的一种变体,在磁盘查找结构中,B+树更适合文件系统的磁盘存储结构。
结构对比:
B-树是平衡多路查找树,全部结点中都包含了待查关键字的有效信息(好比文件磁盘指针)。每一个结点如有n个关键字,则有n+1个指向其余结点的指针。
B+树相比B-树的特色:
1. 数据只出如今叶子结点,B-树每一个结点都包含了数据;
2. 叶子结点之间用指针链接;
3. B+树的高度通常是3;
查找对比:
1. 在相同数量的待查数据下,B+树查找过程当中须要调用的磁盘IO操做要少于普通B-树。因为B+树所在的磁盘存储背景下,所以B+树的查找性能要好于B-树。
2. B+树的查找效率更加稳定,由于全部叶子结点都处于同一层中,并且查找全部关键字都必须走完从根结点到叶子结点的所有历程。所以同一颗B+树中,任何关键字的查找比较次数都是同样的。而B树就不必定了,可能查找到某一个非终结点就结束了。
插入删除对比: B+树与B-树在插入删除操做中的效率是差很少的。
整体评价:在应用背景下,特别是文件结构存储中。B+树的应用要更多,其效率也要比B-树好。