数据结构之AVL树 红黑树

来源:互联网 发布:暴走大事件淘宝店 编辑:程序博客网 时间:2024/06/05 09:05

一 AVL树


1 .基本概念

AVL树的复杂程度真是比二叉搜索树高了整整一个数量级——它的原理并不难弄懂,但要把它用代码实现出来还真的有点费脑筋。下面我们来看看:

1.1  AVL树是什么?

AVL树本质上还是一棵二叉搜索树(因此读者可以看到我后面的代码是继承自二叉搜索树的),它的特点是:

1. 本身首先是一棵二叉搜索树。 

2. 带有平衡条件:每个结点的左右子树的高度之差的绝对值(平衡因子)最多为1。


1.2  为什么要用AVL树?

有人也许要问:为什么要有AVL树呢?它有什么作用呢?

我们先来看看二叉搜索树吧(因为AVL树本质上是一棵二叉搜索树),假设有这么一种极端的情况:二叉搜索树的结点为1、2、3、4、5,也就是:

 1

  \

   2

    \

     3

      \

       4

        \

         5

聪明的你是不是发现什么了呢?呵呵,显而易见——这棵二叉搜索树其实等同于一个链表了,也就是说,它在查找上的优势已经全无了——在这种情况下,查找一个结点的时间复杂度是O(N)!

好,那么假如是AVL树(别忘了AVL树还是二叉搜索树),则会是:

   2

  / \

 1   4

    / \

   3   5

可以看出,AVL树的查找平均时间复杂度要比二叉搜索树低——它是O(logN)。也就是说,在大量的随机数据中AVL树的表现要好得多。


1.3  旋转

假设有一个结点的平衡因子为2(在AVL树中,最大就是2,因为结点是一个一个地插入到树中的,一旦出现不平衡的状态就会立即进行调整,因此平衡因子最大不可能超过2),那么就需要进行调整。由于任意一个结点最多只有两个儿子,所以当高度不平衡时,只可能是以下四种情况造成的:

1. 对该结点的左儿子的左子树进行了一次插入。 

2. 对该结点的左儿子的右子树进行了一次插入。 

3. 对该结点的右儿子的左子树进行了一次插入。 

4. 对该结点的右儿子的右子树进行了一次插入。 

情况1和4是关于该点的镜像对称,同样,情况2和3也是一对镜像对称。因此,理论上只有两种情况,当然了,从编程的角度来看还是四种情况。

第一种情况是插入发生在“外边”的情况(即左-左的情况或右-右的情况),该情况可以通过对树的一次单旋转来完成调整。第二种情况是插入发生在“内部”的情况(即左-右的情况或右-左的情况),该情况要通过稍微复杂些的双旋转来处理。


关于插入后的旋转处理 




二 红黑树

BST的各种操作的时间复杂度是依赖于树的高度,通过使得BST成为红黑树,确保每次对BST进行插入和删除之后,树的高度上限依然是logn.

红黑树,本质上来说就是一棵二叉查找树,而且是平衡的查找二叉树 -- 让BST效率更优

定义

红黑树中每个结点包含五个域:color,key,left,right 和p。通过对一条从根到叶子的路径上各个节点着色方式的限制,红黑树确保没有一条路径会比其他路径长两倍。

如果某结点没有一个子结点或父结点,则该域指向 NIL。

我们把 NIL 视为二叉树的外结点 (叶子),而带关键字的结点视为内结点。

一棵二叉树如果满足下面的红黑性质,则为一棵红黑树:

1) 每个结点或是红的,或是黑的。

2) 根结点是黑的。

3) 每个叶结点 (NIL) 是黑的。

4) 如果一个结点是红的,则它的两个儿子都是黑的。

5) 对每个结点,从该结点到其子孙结点的所有路径上包含相同数目的黑结点。



插入旋转问题 

三种情况

case1:父亲和叔叔都为红   

处理策略
(01) 将“父节点”设为黑色。
(02) 将“叔叔节点”设为黑色。
(03) 将“祖父节点”设为“红色”。
(04) 将“祖父节点”设为“当前节点”(红色节点);即,之后继续对“当前节点”进行操作。

case2:  父节点是红色,兄弟节点是黑色,且当前节点是其父节点的右孩子

措施:父节点左旋(01) 将“父节点”作为“新的当前节点”。
(02) 以“新的当前节点”为支点进行左旋。


case3: 父节点是红色,兄弟节点是黑色,且当前节点是其父节点的左孩子

处理策略
(01) 将“父节点”设为“黑色”。
(02) 将“祖父节点”设为“红色”。
(03) 以“祖父节点”为支点进行右旋。



0 0