树(二叉树、红黑树及左旋右旋等调整操作)

     在分析Java集合类源码的时候涉及到了树的知识,发现树结构要比数组和链表等要复杂的多,所以巩固和补充一下自己这块的知识。

     树(Tree)可以有几种定义。其中一种方式是递归方式。

     树(Tree)是n(n≥0)个结点的有限集T,T为空时称为空树,否则它满足如下两个条件:

  1. 有且仅有一个特定的称为根(Root)的结点;
  2. 其余的结点可分为m(m≥0)个互不相交的子集Tl,T2,…,Tm,其中每个子集本身又是一棵树,并称其为根的子树(SubTree)。

     从定义中可以看出一棵树是N个节点和N-1条边的结合,其中一个节点叫做根。存在N-1条边是这样得出的:每条边都将某个节点连接到它的父亲,而除去根节点外,每个节点都有一个父亲。

    一些树相关的基础定义:

  1. 没有子节点的节点称为树叶,或叶子节点
  2. 有相同父亲的节点称为兄弟节点
  3. 对任意节点的深度是从根到该节点的唯一路径的长度。
  4. 树的指的是从根节点到所有节点的路径中最常的一个。

     二叉树

     二叉树是一棵树,其中每个节点都不能有多余两个儿子。

     树节点可以像如下实现:

1 class Node{
2     Object element;// 存储数据
3     Node left;// 左孩子
4     Node right;// 右孩子
5 }

     二叉查找树

     二叉树的一个重要应用是在查找中的使用。使二叉树称为二叉查找树的性质是,对于树中的每个节点X,它的左子树中所有项的值小于X中的项,而它的右子树中所有的项的值大于X中的项。

     二叉查找树的插入、删除节点

     插入操作沿着树查找,如果找到了要插入到树中的X,则什么都不做。否则将X插入到遍历的路径的最后一个节点上。将节点插入到某个节点上只需要修改被插入节点的对应的left或right引用即可。

     删除操作要比查找复杂的多,因为如果删除的不是叶子节点,需要将被删除节点的子孙节点重新连接到删除后的树中并保持查找树的性质。分三种情况讨论删除节点的内容:

     删除的节点是一个叶子节点,那么立即删除该节点。

     删除的节点有一个孩子,用这个孩子节点代替被删除节点,即将被删除节点的父亲节点对被删除节点的引用修改为对它孩子的引用,实现“绕过”被删除节点。

     删除的节点有两个孩子,一般的删除策略是用其右子树的最小数据(右子树中“最左”的一个节点)代替该删除的节点,并递归的删除那个节点。因为右子树的最小数据不会有左子树,所以删除要比第一次要简单。

     AVL

     AVL树是带有平衡条件的二叉查找树。这个平衡条件必须容易保持,而且它保证树的深度必须是O(log N)。一颗AVL树是其每个节点的左子树和右子树的高度最多差1的二叉查找树(空树的高度定义为-1)。

     对AVL树进行插入操作可能会使其失去平衡的条件,但这可以通过对树进行简单的修正来保持其平衡的属性,这种操作称之为旋转

     在插入后,只有那些从插入点到根节点的路径上的平衡可能被改变,因此只有这些节点的子树可能发生变化。当沿着这条路径上行到根并更新平衡信息时,可以发现一个节点,它的新平衡破坏了AVL条件。把这个节点记为a。因为任意节点最多有两个儿子,因此有下面四种情况:

  1. 对a的左儿子的左子树进行一次插入
  2. 对a的左儿子的右子树进行一次插入
  3. 对a的右儿子的左子树进行一次插入
  4. 对a的右儿子的右子树进行一次插入

     以上四种情况又可以归纳为两种操作,即插入发生在“外部”(左-左(1),右-右(4));插入发生在“内部”(左-右(2),右-左(3))。对于“外部”情况只需要一次单旋转就可以完成调整,而“内部”需要通过双旋转来处理。

     单旋转

     下图显示了对于情形1如何进行单旋转。左边是旋转前,右边是旋转后。在左边的树中,子树X中新插入了一个结点使得结点k2不再满足AVL平衡性质,因为它的左子树比右子树深2层(图中间的虚线代表树的各层)。

树(二叉树、红黑树及左旋右旋等调整操作)

     下图演示应于“情形4”的单旋转修正:

树(二叉树、红黑树及左旋右旋等调整操作)

     双旋转

     对于“内部”情况,但旋转是无法处理的,如下图。

树(二叉树、红黑树及左旋右旋等调整操作)

     此时需要使用双旋转(下面是两种情况的双旋转处理):

树(二叉树、红黑树及左旋右旋等调整操作)

树(二叉树、红黑树及左旋右旋等调整操作)

    红黑树

    历史上AVL树流行的另一变种是红黑树。红黑树具有下列着色性质的二叉查找树:

  1. 每一个节点或者着成红色,或者着成黑色。
  2. 根是黑色的。
  3. 如果一个节点是红色的,那么它的子节点必须是黑色的。
  4. 一个节点到一个null引用的每一条路径必须包含相同数量的黑色节点。

     下面是一颗红黑树:

树(二叉树、红黑树及左旋右旋等调整操作)

     在将新的节点插入到红黑树中时,如果将它涂成黑色,那么肯定违反了条件4,因为会建立一条更长的黑色节点的路径。因此这项必须涂成红色。如果它的父节点是黑色的,则插入完成,如果它的父节点已经是红色的,那么连续的红色节点违反了条件3。这种情况下必须调整概述以确保维持红黑树的性质。用于完成这项任务的基本操作是颜色的改变和树的旋转。

     由上面的内容可以退出插入节点时默认都是红色的,如果插入节点后破坏了树的平衡,需要通过旋转重新使其达到平衡,之后修改颜色使其保证红黑树的性质。

     红黑树更详细的介绍可以看下面的几篇博客,都总结的比我的要好的多,就不再累述了。

     http://blog.csdn.net/v_JULY_v/archive/2010/12/29/6105630.aspx

     http://blog.csdn.net/v_JULY_v/archive/2010/12/31/6109153.aspx

     http://blog.csdn.net/v_JULY_v/article/details/6124989

 

如果本文对您有帮助,点一下右下角的“推荐”
上一篇:MVC中,提交后界面Model值未能更新的原因及优化。


下一篇:Script Lab 07:引入控件,Excel 基础操作(5)