各种树的特点和区别

BST树

       即二叉搜索树:

       1.所有非叶子结点至多拥有两个儿子(Left和Right);

       2.所有结点存储一个关键字;

       3.非叶子结点的左指针指向小于其关键字的子树,右指针指向大于其关键字的子树;

       如:

       

       BST树的搜索,从根结点开始,如果查询的关键字与结点的关键字相等,那么就命中;

否则,如果查询关键字比结点关键字小,就进入左儿子;如果比结点关键字大,就进入

右儿子;如果左儿子或右儿子的指针为空,则报告找不到相应的关键字;

       如果BST树的所有非叶子结点的左右子树的结点数目均保持差不多(平衡),那么B树

的搜索性能逼近二分查找;但它比连续内存空间的二分查找的优点是,改变BST树结构

(插入与删除结点)不需要移动大段的内存数据,甚至通常是常数开销;

       如:

      

   但BST树在经过多次插入与删除后,有可能导致不同的结构:

   右边也是一个BST树,但它的搜索性能已经是线性的了;同样的关键字集合有可能导致不同的

树结构索引;所以,使用BST树还要考虑尽可能让BST树保持左图的结构,和避免右图的结构,也就

是所谓的“平衡”问题;      

       
AVL平衡二叉搜索树
定义:平衡二叉树或为空树,或为如下性质的二叉排序树:
  (1)左右子树深度之差的绝对值不超过1;
  (2)左右子树仍然为平衡二叉树.
平衡因子BF=左子树深度-右子树深度.
平衡二叉树每个结点的平衡因子只能是1,0,-1。若其绝对值超过1,则该二叉排序树就是不平衡的。
如图所示为平衡树和非平衡树示意图:



 

RBT 红黑树

AVL是严格平衡树,因此在增加或者删除节点的时候,根据不同情况,旋转的次数比红黑树要多;
红黑是弱平衡的,用非严格的平衡来换取增删节点时候旋转次数的降低;
所以简单说,搜索的次数远远大于插入和删除,那么选择AVL树,如果搜索,插入删除次数几乎差不多,应该选择RB树。

红黑树上每个结点内含五个域,color,key,left,right,p。如果相应的指针域没有,则设为NIL。
一般的,红黑树,满足以下性质,即只有满足以下全部性质的树,我们才称之为红黑树:
1)每个结点要么是红的,要么是黑的。
2)根结点是黑的。
3)每个叶结点,即空结点(NIL)是黑的。
4)如果一个结点是红的,那么它的俩个儿子都是黑的。
5)对每个结点,从该结点到其子孙结点的所有路径上包含相同数目的黑结点。
下图所示,即是一颗红黑树:






 

B-树

       是一种平衡多路搜索树(并不是二叉的):

       1.定义任意非叶子结点最多只有M个儿子;且M>2;

       2.根结点的儿子数为[2, M];

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

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

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

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

       7.非叶子结点的指针:P[1], P[2], …, P[M];其中P[1]指向关键字小于K[1]的

子树,P[M]指向关键字大于K[M-1]的子树,其它P[i]指向关键字属于(K[i-1], K[i])的子树;

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

       如:(M=3

       B-树的搜索,从根结点开始,对结点内的关键字(有序)序列进行二分查找,如果

命中则结束,否则进入查询关键字所属范围的儿子结点;重复,直到所对应的儿子指针为

空,或已经是叶子结点;

B-树的特性:

       1.关键字集合分布在整颗树中;

       2.任何一个关键字出现且只出现在一个结点中;

       3.搜索有可能在非叶子结点结束;

       4.其搜索性能等价于在关键字全集内做一次二分查找;

       5.自动层次控制;

       由于限制了除根结点以外的非叶子结点,至少含有M/2个儿子,确保了结点的至少

利用率,其最底搜索性能为:

    

       其中,M为设定的非叶子结点最多子树个数,N为关键字总数;

       所以B-树的性能总是等价于二分查找(与M值无关),也就没有B树平衡的问题;

       由于M/2的限制,在插入结点时,如果结点已满,需要将结点分裂为两个各占

M/2的结点;删除结点时,需将两个不足M/2的兄弟结点合并;

 

 

B+树

       B+树是B-树的变体,也是一种多路搜索树:

       1.其定义基本与B-树同,除了:

       2.非叶子结点的子树指针与关键字个数相同;

       3.非叶子结点的子树指针P[i],指向关键字值属于[K[i], K[i+1])的子树

(B-树是开区间);

       5.为所有叶子结点增加一个链指针;

       6.所有关键字都在叶子结点出现;

       如:(M=3)

   B+的搜索与B-树也基本相同,区别是B+树只有达到叶子结点才命中(B-树可以在

非叶子结点命中),其性能也等价于在关键字全集做一次二分查找;

       B+的特性:

       1.所有关键字都出现在叶子结点的链表中(稠密索引),且链表中的关键字恰好

是有序的;

       2.不可能在非叶子结点命中;

       3.非叶子结点相当于是叶子结点的索引(稀疏索引),叶子结点相当于是存储

(关键字)数据的数据层;

       4.更适合文件索引系统;比如对已经建立索引的数据库记录,查找10<=id<=20,那么只要通过根节点搜索到id=10的叶节点,之后只要根据叶节点的链表找到第一个大于20的就行了,比B-树在查找10到20内的每一个时每次都从根节点出发查找提高了不少效率。

  

B*树

       是B+树的变体,在B+树的非根和非叶子结点再增加指向兄弟的指针;

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

(代替B+树的1/2);

       B+树的分裂:当一个结点满时,分配一个新的结点,并将原结点中1/2的数据

复制到新结点,最后在父结点中增加新结点的指针;B+树的分裂只影响原结点和父

结点,而不会影响兄弟结点,所以它不需要指向兄弟的指针;

       B*树的分裂:当一个结点满时,如果它的下一个兄弟结点未满,那么将一部分

数据移到兄弟结点中,再在原结点插入关键字,最后修改父结点中兄弟结点的关键字

(因为兄弟结点的关键字范围改变了);如果兄弟也满了,则在原结点与兄弟结点之

间增加新结点,并各复制1/3的数据到新结点,最后在父结点增加新结点的指针;

       所以,B*树分配新结点的概率比B+树要低,空间使用率更高;

  

小结

       B树:二叉树,每个结点只存储一个关键字,等于则命中,小于走左结点,大于

走右结点;

       B-树:多路搜索树,每个结点存储M/2到M个关键字,非叶子结点存储指向关键

字范围的子结点;

       所有关键字在整颗树中出现,且只出现一次,非叶子结点可以命中;

       B+树:在B-树基础上,为叶子结点增加链表指针,所有关键字都在叶子结点

中出现,非叶子结点作为叶子结点的索引;B+树总是到叶子结点才命中;

       B*树:在B+树基础上,为非叶子结点也增加链表指针,将结点的最低利用率

从1/2提高到2/3;


B+/B*Tree应用

数据库索引–索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。



数据库索引–表数据文件本身就是按B+Tree组织的一个索引结构,这棵树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键。



倒排索引–也可以由B树及其变种实现但不一定非要B树及其变种实现,如lucene没有使用B树结构,因此lucene可以用二分搜索算法快速定位关键词。实现时,lucene将下面三列分别作为词典文件(Term Dictionary)、频率文件(frequencies)、位置文件 (positions)保存。其中词典文件不仅保存有每个关键词,还保留了指向频率文件和位置文件的指针,通过指针可以找到该关键字的频率信息和位置信息。   


   
   
  1. 关键词 文章号[出现频率] 出现位置   
  2. guangzhou 1[ 2] 36   
  3. he 2[ 1] 1   
  4. i 1[ 1] 4   
  5. live 1[ 2] 25,
  6. 2[ 1] 2   
  7. shanghai 2[ 1] 3   
  8. tom 1[ 1] 1


参考文章

B-树和B+树的应用:数据搜索和数据库索引 http://blog.csdn.net/hguisu/article/details/7786014

B树、B-树、B+树、B*树 http://www.cnblogs.com/oldhorse/archive/2009/11/16/1604009.html

            </div>
设计模式是一套被反复使用、多数人知晓的解决特定问题的方法,用于解决在软件开发过程中的一般问题,其中大部分是针对面向对象软件设计领域。根据用途和实现方式的不同,设计模式可分为以下几种类型: 1. 创建型模式:主要处理对象的创建,帮助我们在系统中创建对象。其中包括单例模式、工厂方法模式、抽象工厂模式、建造者模式和原型模式。 - 单例模式: 保证一个类只有一个实例,并提供一个全局访问点。 - 工厂方法模式: 定义一个用于创建对象的接口,让子类决定将哪个类实例化。 - 抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。 - 建造者模式: 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。 - 原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。 2. 结构型模式:描述如何组合各种对象以达到新功能的目的,通常用于解决对象的组合问题,其中包括适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式和代理模式。 - 适配器模式:将一个类的接口转换成客户希望的接口,让原本不兼容的类可以一起工作。 - 桥接模式:将抽象部分与它的实现部分分离,使它们都可以独立地变化。 - 装饰模式:动态地给一个对象添加一些额外的职责,就增加功能而言,比生成子类更为灵活。 - 组合模式:将对象组合成形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。 - 外观模式:为子系统中的一组接口提供一个统一的接口,定义一个高层接口,使得该子系统更加容易使用。 - 享元模式:运用共享技术有效地支持大量细粒度的对象,避免对象数量的爆炸性增长。 - 代理模式:为其他对象提供一个代理以控制对这个对象的访问。 3. 行为型模式:解决类或对象间的通信问题,其中包括模板方法模式、策略模式、命令模式、责任链模式、状态模式、观察者模式、中介者模式、迭代器模式和访问者模式。 - 模板方法模式:定义一个操作中的算法的骨架,将一些步骤延迟到子类中。 - 策略模式:定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。 - 命令模式:将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化。 - 责任链模式:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。 - 状态模式:允许一个对象在其内部状态改变时改变它的行为。 - 观察者模式:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知。 - 中介者模式:用一个中介对象来封装一系列的对象交互,中介者使各个对象不需要显式地相互作用。 - 迭代器模式:提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。 - 访问者模式:封装作用于某种数据结构中的各种不同的操作,可以在不改变数据结构的前提下定义作用于这些元素的新的操作。 这些设计模式各有各自的特点区别,我们在软件设计时可以根据不同的需求选择合适的设计模式来解决问题。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值