如果树中插入的是随机数据,则执行效果很好。但是,如果插入的是有序的数据的数据,速度就会变得特别慢。**因为当插入的数值有序时,二叉搜索树就退化成了链表的形式了。**此时,也可以说这棵树是非平衡树,那么它的快速查找(插入,删除)指定数据项的能力就丧失了。
如下图所示:
平衡树,即平衡二叉树(Balanced Binary Tree),具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
图9-1是一种极端不平衡的树,它的事件复杂度为O(N)。所以搜索部分非平衡树的时间介于O(N)和O(logN)之间,这取决于树的不平衡程序。
在插入过程中,对于一个要插入的数据项,插入例程要检查不会破坏树一定的特征。如果破坏了,程序就会进行纠正,根据需要更改树的结构。通过维持树的特征,保证了树的平衡。我们将这种方式成为旋转。
红黑树是一种自平衡的二叉搜索树
它的规则如下:
为什么红黑规则能保证树是平衡的?
如果一条路径上的节点数比另一条路径上的节点数多一个以上(不包含1个),那它要么有更多的黑色节点,违背了规则4。要么有两个相邻接的红色节点,违背了规则3。
如果节点只有右子节点,那么它的空缺的左子节点就是空子节点。反之亦然。
如下图所示:
旋转要做的两件事情:
向左旋转或者向右旋转
选择一个节点作为旋转的“顶端”。如果做一次右旋,这个“顶端”节点将会向下和向右移动到它右子节点的位置。它的左子节点将会上移到它原来的位置。
注意事项:
如果做右旋,顶端节点必须有一个左子节点。否则,将没有节点旋转到顶端节点原来所在的位置。类似,左旋,顶端节点必须有一个右子节点。
需要解决以下三个问题:
红黑树的插入例程的开始时所做的事情和普通的二叉搜索树所做的基本一样:沿着根朝插入点位置走,在每一个节点处通过比较节点的关键字相对大小来决定向左走还是向右走。
查找插入新节点的位置,为了不违反颜色规则,在必要的时候需要进行颜色变换。
规则:每当查找例程遇到一个有两个红色子节点的黑色节点时,它必须把子节点变为黑色,而把父节点变为红色(除非父节点为根节点,根总是黑色的)
如下图所示:
这种颜色变换之后带来的好处:
新数据项的插入可能会违背红黑规则,因此,在插入之后,必须要检测是否违背规则,并采用相应的措施。
如下图所示,有四种情况
如果节点X在P的一侧与P在G的一侧相同,则X就是一个外侧子孙节点。相反,如果节点X在P的一侧,而P在G的另一侧,则X就是一个内侧子孙节点。
有了上述概念之后,四种情况归纳为三种:
新插入的X总是红色的
如果P是黑色,什么事情也不用做。因为刚刚插入的节点总是红色的。如果它的父节点是黑色,则没有红色节点连接红色节点的冲突(规则3)。并且也不会增加黑色节点的数目(规则4)。因此,不需要做其他事情,插入完成。
改变X的祖父节点G(25)的颜色
改变X的父节点P(12)的颜色
以X的祖父节点G(25)为顶旋转,向X(6)上升的方向。做一次右旋操作
如下图所示:
当X(18)是内侧子孙节点时,其解决技巧是执行两次旋转。第一次把X由内侧子孙节点变为外侧子孙节点。再按照 外侧子孙节点的情况进行处理。
操作步骤如下(一般是先着色,再旋转):
在下行路途中查找插入点时所做的旋转
在向下的路径上有两种旋转的可能性,分别对应前面描述的在插入阶段的可能性2和可能性3。违背规则的节点可能是一个外侧子孙节点,也可能是一个内侧子孙节点。
注意:在插入12和6时已经做了两次颜色变换,否则不能插入新节点
现在要插入值为3的节点。必须对节点12以及它的子节点6和18做颜色变换。
变换后如下图:
纠正这种违规情况和前面讨论的插入的可能情况2类似。必须要执行两次颜色变换和一次旋转。
以刚刚做了颜色变换三角形的顶端节点为X(12),P(25),G(50)
插入一个新节点28,需要颜色变换。此时25和37都是红色。G(50),P(25),X(37)
为了解决红-红冲突。需要做和可能性3相同的两次颜色变换和两次旋转。
在插入的过程中,保证树的红黑特性,可以取得树的平衡
在普通二叉搜索树中编写删除操作的代码比编写插入操作要难得多。在红黑树也是这样的,并且在删除过程由于需要在节点删除之后恢复树的红黑正确性,变得更复杂。
由于删除的复杂性,在实际操作中都是规避真正的删除节点。就是为删除的节点做个标记而不实际的删除它。任何找到该节点的查找例程都知道不用报告已找到该节点
红黑树的查找,插入和删除的时间复杂度为O(logN)。在红黑树的查找时间和在普通的二叉搜索树查找时间几乎一样,因为在查找的过程中并没有应用到红黑树的特征,额外的开销只是每一个节点的存储空间都稍微增加了一点,来存储红黑树的颜色(boolean变量)。
插入和删除的时间要增加一个常数因子,因为不得不在下行的路径上和插入点执行颜色变换和旋转。平均起来,一次插入大约需要一次旋转。因此,插入的时间复杂度还是O(logN),但是比普通的二叉搜索树要慢。
大多数应用中,查找的数次比插入和删除的次数多,所以使用红黑树取代普通的二叉搜索树总体不会增加太多的时间开销。
红黑树的优点是对有序数据的操作不会慢到O(N)的时间复杂度
AVL树:在AVL树中,每个节点存储一个额外的数据,它的左子树和右子树的高度差,这个差值不会大于1.也就是说,节点的左子树的高度和右子树的高度相差不会大于1层。
插入之后,检查新节点的插入点所在的最低子树的根。如果它的子节点的高度相差大于1,执行一次或者两次旋转使它们的高度相同。然后算法上移,检查上面的节点,必要时均衡高度。这个检测检查所有路径一直向上,直到根为止。
AVL树查找的时间复杂度为O(logN),因为树一定是平衡的。但是,由于插入(删除)一个节点需要扫描两趟树,一次向下查找插入点,一次向上平衡树,所以:
AVL树不如红黑树效率高,也不如红黑树常用。