树结构

数据结构中有很多树的结构,其中包括二叉树、二叉搜索树、2-3树、红黑树等等。本文中对数据结构中常见的几种树的概念和用途进行了汇总。

在介绍各种树之前,先了解一下关于有关树的一些基本术语以便理解下文:

⑴ 结点的度:树中一个结点的子节点个数称为结点的度 
⑵ 树的度:树中结点的最大度数称为树的度 
⑶ 分支结点:度大于0的结点(又称为非终端结点) 
⑷ 叶子结点:度等于0(没有子女结点)的结点称为叶子结点。 
⑸结点的层次:从树根开始定义,根结点为第一层 (有的也把根结点当为第0层) 
⑹结点的深度:是从根节点开始自顶向下逐层累加的 
⑺ 节点的高度:从根节点开始自底向上逐层累加 
⑻ 树的高度(又叫深度):是树中结点的最大层数 
⑼ 路径和路径长度:树中两个结点之间的路径是由这两个结点之间所经过的结点序列构成的。路径长度是路径上所经过的边的个数。 
⑽有序树:如果将树中结点的各子树看成从左至右是有次序的(即不能互换),则称该树是有序数,否则称为无序树 
⑾森林:是m(>=0)棵互不相交的树的集合。森林的概念与树的概念十分相近,因为只要把树的跟删除就成了森林。

1、二叉树

二叉树示例

                                

二叉树定义:二叉树的每个结点至多只有二棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒。二叉树的第i层至多有2i-1个结点;深度为k的二叉树至多有2k-1个结点;对任何一棵二叉树T,如果其终端结点数为n0,度为2的结点数为n2,则n0=n2+1。二叉树又有满二叉树和完全二叉树

二叉树的性质

  1) 在非空二叉树中,第i层的结点总数不超过2i-1, i>=1;

  2) 深度为h的二叉树最多有2h-1个结点(h>=1),最少有h个结点;

  3) 对于任意一棵二叉树,如果其叶结点数为N0,而度数为2的结点总数为N2,则N0=N2+1;

  4) 具有n个结点的完全二叉树的深度为log2(n+1);

  5) 有N个结点的完全二叉树各结点如果用顺序方式存储,则结点之间有如下关系:

    若I为结点编号则 如果I>1,则其父结点的编号为I/2;

    如果2I<=N,则其左儿子(即左子树的根结点)的编号为2I;若2I>N,则无左儿子;

    如果2I+1<=N,则其右儿子的结点编号为2I+1;若2I+1>N,则无右儿子。

  6) 给定N个节点,能构成h(N)种不同的二叉树,其中h(N)为卡特兰数的第N项,h(n)=C(2*n, n)/(n+1)。

  7) 设有i个枝点,I为所有枝点的道路长度总和,J为叶的道路长度总和J=I+2i。

 

完全二叉树和满二叉树示例

       

满二叉树定义:除最后一层无任何子节点外,每一层上的所有结点都有两个子结点。也可以这样理解,除叶子结点外的所有结点均有两个子结点。节点数达到最大值,所有叶子结点必须在同一层上。

满二叉树性质

  1) 一颗树深度为h,最大层数为k,深度与最大层数相同,k=h;

  2) 叶子数为2h;

  3) 第k层的结点数是:2k-1;

  4) 总结点数是:2k-1,且总节点数一定是奇数。

完全二叉树定义:若设二叉树的深度为h,除第 h 层外,其它各层 (1~(h-1)层) 的结点数都达到最大个数,第h层所有的结点都连续集中在最左边,这就是完全二叉树。

注:完全二叉树是效率很高的数据结构,堆是一种完全二叉树或者近似完全二叉树,所以效率极高,像十分常用的排序算法、Dijkstra算法、Prim算法等都要用堆才能优化,二叉排序树的效率也要借助平衡性来提高,而平衡性基于完全二叉树。

2、二叉查找树

 二叉查找树的定义:又称为是二叉排序树(Binary Sort Tree)或二叉搜索树。二叉排序树或者是一棵空树,或者是具有下列性质的二叉树:

          1) 若左子树不空,则左子树上所有结点的值均小于它的根结点的值;

          2) 若右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值;

          3) 左、右子树也分别为二叉排序树;

          4) 没有键值相等的节点。

二叉查找树的性质:对二叉查找树进行中序遍历,即可得到有序的数列。

二叉查找树的时间复杂度:它和二分查找一样,插入和查找的时间复杂度均为O(logn),但是在最坏的情况下仍然会有O(n)的时间复杂度。原因在于插入和删除元素的时候,树没有保持平衡(比如,我们查找上图(b)中的“93”,我们需要进行n次查找操作)。我们追求的是在最坏的情况下仍然有较好的时间复杂度,这就是平衡查找树设计的初衷。

二叉查找树的高度决定了二叉查找树的查找效率。

二叉查找树的插入过程如下:

1) 若当前的二叉查找树为空,则插入的元素为根节点;

2) 若插入的元素值小于根节点值,则将元素插入到左子树中;

3) 若插入的元素值不小于根节点值,则将元素插入到右子树中。

二叉查找树的删除,分三种情况进行处理:

1) z为叶子节点,直接删除该节点,再修改其父节点的指针(注意分是根节点和不是根节点),如图a;

2) z为单支节点(即只有左子树或右子树)。让p的子树与p的父亲节点相连,删除z即可(注意分是根节点和不是根节点),如图b;

3) z的左子树和右子树均不空。找到z的后继y,因为y一定没有左子树,所以可以删除y,并让y的父亲节点成为y的右子树的父亲节点,并用y的值代替z的值;或者方法二是找到z的前驱x,x一定没有右子树,所以可以删除x,并让x的父亲节点成为y的左子树的父亲节点。如图c。

  

3、平衡二叉树

对于一般的二叉搜索树(Binary Search Tree),其期望高度(即为一棵平衡树时)为log2n,其各操作的时间复杂度O(log2n)同时也由此而决定。但是,在某些极端的情况下(如在插入的序列是有序的时),二叉搜索树将退化成近似链或链,此时,其操作的时间复杂度将退化成线性的,即O(n)。我们可以通过随机化建立二叉搜索树来尽量的避免这种情况,但是在进行了多次的操作之后,由于在删除时,我们总是选择将待删除节点的后继代替它本身,这样就会造成总是右边的节点数目减少,以至于树向左偏沉。这同时也会造成树的平衡性受到破坏,提高它的操作的时间复杂度。于是就有了我们下边介绍的平衡二叉树:

  3.1、平衡二叉树之AVL树

      平衡二叉树(AVL树)定义:平衡二叉树(Balanced Binary Tree)又被称为AVL树(有别于AVL算法),平衡二叉树或者是一棵空树,或者是具有以下性质的二叉排序树:(1)它的左子树和右子树的高度之差绝对值不超过1;(2)它的左子树和右子树都是平衡二叉树。

      AVL树具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。在平衡二叉搜索树中,我们可以看到,其高度一般都良好地维持在O(log2n),其各操作的时间复杂度(O(log2n))同时也由此而决定,大大降低了操作的时间复杂度。另外,最小二叉平衡树的节点的公式如下 F(n)=F(n-1)+F(n-2)+1 ,这个类似于一个递归的数列,可以参考Fibonacci数列,1是根节点,F(n-1)是左子树的节点数量,F(n-2)是右子树的节点数量。

AVL树之所以能解决二叉查找树退化成链表的问题,就是得益于自旋转操作,它把插入,查找,删除的时间复杂度最好情况和最坏情况都维持在O(logN)。但是频繁旋转会使插入和删除牺牲掉O(logN)左右的时间,不过相对普通二叉查找树来说,时间上稳定了很多。

  3.2、平衡二叉树之红黑树

         红黑树的定义:红黑树也是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组。

      红黑树的性质:红黑树是每个节点都带有颜色属性的二叉查找树,颜色为红色或黑色。在二叉查找树强制的一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求:

              性质1. 节点是红色或黑色。

              性质2. 根是黑色。

              性质3. 所有叶子都是黑色(叶子是NIL节点)。

              性质4. 每个红色节点必须有两个黑色的子节点。(从每个叶子到根的所有路径上不能有两个连续的红色节点。)

              性质5. 从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点。

              这些性质保证了这始终是一颗平衡的二叉查找树

              下面是一个具体的红黑树的图例:

                                                       

 4、B树

  B树也是一种用于查找的平衡树,但是它不是二叉树。

  B树的定义:B树(B-tree)是一种树状数据结构,能够用来存储排序后的数据。这种数据结构能够让查找数据、循序存取、插入数据及删除的动作,都在对数时间内完成。B树,概括来说是一个一般化的二叉查找树,可以拥有多于2个子节点。与自平衡二叉查找树不同,B-树为系统最优化大块数据的读和写操作。B-tree算法减少定位记录时所经历的中间过程,从而加快存取速度。这种数据结构常被应用在数据库和文件系统的实作上。

  在B树中查找给定关键字的方法是,首先把根结点取来,在根结点所包含的关键字K1,…,Kn查找给定的关键字(可用顺序查找或二分查找法),若找到等于给定值的关键字,则查找成功;否则,一定可以确定要查找的关键字在Ki与Ki+1之间,Pi为指向子树根节点的指针,此时取指针Pi所指的结点继续查找,直至找到,或指针Pi为空时查找失败。

  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树示例:

    

5、B+树

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

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

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

  3) 非叶子结点的子树指针P[i],指向关键字值属于[K[i], K[i+1])的子树(B-树是开区间);

  4) 为所有叶子结点增加一个链指针;

  5) 所有关键字都在叶子结点出现;

  下图为M=3的B+树的示意图:

    

  B+树的搜索与B树也基本相同,区别是B+树只有达到叶子结点才命中(B树可以在非叶子结点命中),其性能也等价于在关键字全集做一次二分查找;

  B+的性质:

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

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

  3.非叶子结点相当于是叶子结点的索引(稀疏索引),叶子结点相当于是存储(关键字)数据的数据层;

  4.更适合文件索引系统。

6、B*树

  B*树是B+树的变体,在B+树的非根和非叶子结点再增加指向兄弟的指针,将结点的最低利用率从1/2提高到2/3。

  B*树如下图所示:

  

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

  B+树的分裂:当一个结点满时,分配一个新的结点,并将原结点中1/2的数据复制到新结点,最后在父结点中增加新结点的指针;B+树的分裂只影响原结点和父结点,而不会影响兄弟结点,所以它不需要指向兄弟的指针;

  B*树的分裂:当一个结点满时,如果它的下一个兄弟结点未满,那么将一部分数据移到兄弟结点中,再在原结点插入关键字,最后修改父结点中兄弟结点的关键字(因为兄弟结点的关键字范围改变了);如果兄弟也满了,则在原结点与兄弟结点之间增加新结点,并各复制1/3的数据到新结点,最后在父结点增加新结点的指针;

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

7、Tire树

Tire树称为字典树,又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。 

  Tire树的三个基本性质:
  1) 根节点不包含字符,除根节点外每一个节点都只包含一个字符;
  2) 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串;
  3) 每个节点的所有子节点包含的字符都不相同。

  Tire树的应用:

  1) 串的快速检索

  给出N个单词组成的熟词表,以及一篇全用小写英文书写的文章,请你按最早出现的顺序写出所有不在熟词表中的生词。

在这道题中,我们可以用数组枚举,用哈希,用字典树,先把熟词建一棵树,然后读入文章进行比较,这种方法效率是比较高的。

  2) “串”排序

  给定N个互不相同的仅由一个单词构成的英文名,让你将他们按字典序从小到大输出。用字典树进行排序,采用数组的方式创建字典树,这棵树的每个结点的所有儿子很显然地按照其字母大小排序。对这棵树进行先序遍历即可。

  3) 最长公共前缀

  对所有串建立字典树,对于两个串的最长公共前缀的长度即他们所在的结点的公共祖先个数,于是,问题就转化为求公共祖先的问题。

 

posted @ 2018-06-12 15:55  世界辣么大  阅读(1615)  评论(0编辑  收藏  举报