红黑树,你还要我怎样

关于红黑树的相关重点,原博主总结得非常好,我只做了细微的修改!

【转载】http://blog.csdn.net/silangquan/article/details/18322087

红黑树很重要,STL中的set、map、multimap、multiset这些关联容器都是红黑树实现的,但能红黑树的实现难度非常大,所以,我们记住如下的重点就好。

关于红黑树的基本概念,请见http://blog.csdn.net/BillCYJ/article/details/78986279

1.stl中的set底层用的什么数据结构?

红黑树

2.红黑树的数据结构怎么定义的?

enum Color  
{  
    RED = 0,  
    BLACK = 1  
};  

struct RBTreeNode  
{  
    struct RBTreeNode*left, *right, *parent;  
    int key;//关键字
    int data;//对于map和multimap来说,还有data来存储关键字对应的值。
    Color color;//记录该点的颜色
};  

3.红黑树有哪些性质?

只有满足以下全部性质的树,我们才称之为红黑树:
1)每个结点要么是红的,要么是黑的。
2)根结点是黑的。
3)每个叶结点(叶结点即指树尾端NIL指针或NULL结点)是黑的。
4)如果一个结点是红的,那么它的俩个儿子都是黑的。
5)对于任一结点而言,从根节点到叶结点的每条路,黑色结点数相同,即相同的黑色高度。

4.红黑树的各种操作的时间复杂度是多少?

红黑树的上述5条性质使一棵n个结点的RBT保持了lg(n)的高度和最坏为O(lg n)的时间复杂度。
所以,能保证在最坏情况下,基本的动态几何操作的时间均为O(lgn)。

5.红黑树相比于BST和AVL树有什么优点?

总的来说:
红黑树能够以O(log2 n)的时间复杂度进行搜索、插入、删除操作。此外,由于它的设计,任何不平衡都会在三次旋转之内解决。当然,还有一些更好的,但实现起来更复杂的数据结构能够做到一步旋转之内达到平衡,但红黑树能够给我们一个比较“便宜”的解决方案了。

相比于BST:
因为红黑树可以能确保树的最长路径不大于两倍的最短路径的长度,所以可以看出它的查找效果是有最低保证的。在最坏的情况下也可以保证O(logN)的,这是要好于二叉查找树的。因为二叉查找树最坏情况可以让查找达到O(N)。

相比于AVL:
AVL是严格平衡树,因此在增加或者删除节点的时候,根据不同情况,旋转的次数比RBT要多;RBT是弱平衡的,牺牲了严格的高度平衡为代价,用非严格的平衡来换取旋转次数的降低(增删节点的时候) 。 所以,在插入和删除中所做的后期维护操作肯定会比红黑树要耗时得多,但是他们的查找效率都是O(logN),所以红黑树应用面比AVL树更广。现在有两种选择方式:(综合着来考虑呗)

  1. 若搜索的次数远远大于插入和删除,就选AVL树;若搜索、插入、删除次数差不多,就选RBT。
  2. 但插入AVL树和红黑树的速度又取决于你所插入的数据。若你的数据分布较好,则比较宜于采用 AVL树(例如随机产生系列数),但若你想处理比较杂乱的情况,则红黑树是比较快的。

6.红黑树相对于哈希表,在选择使用的时候有什么依据?

权衡四个因素: 查找速度,数据量, 内存使用,数据的有序性。

综述: hash表比红黑树用了更多的内存,换取了更快的速度,但各有优劣。

  具体来说,hash表查找速度会比红黑树快,而且查找速度基本和数据量大小无关,属于常数级别,但hash对象占用的内存hash表的构造速度(hash表是由hash函数构造的)却和数据量大小相关。而红黑树的查找速度是log(n)级别,但不一定常数就比log(n) 小。
  所以,如果数据完全是静态的,做一个哈希表,性能可能会更好一些。但如果数据不是静态的:1.如果你重视效率,特别是在元素达到一定数量级时,考虑hash表;2.如果你重视内存,希望程序尽可能少消耗内存,那么小心hash表,因为当你的hash对象特别多时,内存消耗大得恐怖,并且hash表的构造速度较慢。
  在实际的系统中,例如,需要使用动态规则的防火墙系统,使用红黑树而不是hash表被实践证明具有更好的伸缩性。
  数据的有序性:就hash表而言,它里面的元素是无序的,且不一定与输入顺序相同,因为经过了hash函数的映射;就红黑树而言,它里面的元素是有序的。红黑树的有序性在很多应用中都会简化很多的操作。另外:STL中的map是红黑树实现的,unorderedmap却是hash表实现的,具体请见:map和unordered_map的差别和使用

7.如何找到第i小的结点?如何扩展红黑树来获得比某个结点小的元素有多少个?

特别有意思的两个问题

这其实就是求节点元素的顺序统计量,当然任意的顺序统计量都可在O(lgn)时间内完成。
在每个节点添加一个size域,表示以结点 x 为根的子树的结点树的大小,则有size[x] = size[[left[x]] + size [right[x]] + 1;
这时候红黑树就变成了一棵顺序统计树(可通过中序遍历来获得有序的序列)。利用size域可以做两件事:

1.找到树中第i小的结点

OS-SELECT(x,i)//x为根结点
r = size[left[x]] + 1;  
if r == i  
     return x  
else if i < r  
     return OS-SELECT(left[x], i)  
else return OS-SELECT(right[x],  i)  

思路:size[left[x]]表示比x小的个数,递归调用的深度不会超过O(lgn)。

2.确定某个结点之前有多少个结点,也就是我们要解决的问题

OS-RANK(x,T)//x为目标结点,T为红黑树
r = x.left.size + 1;//r是中间变量
y = x;//接下来要判断x是不是其根节点的右子结点

//循环的意义在于:从目标结点开始往根结点走,一路上统计小于其的个数
while y != T.root
         if y == y.p.right//判断y是不是其根结点的右子结点
                 r = r + y.p.left.size +1//+1是为了把统计y.p这个结点
         y = y.p  
return r  

思路:有两种情况:1.目标结点是其根结点的左子结点,那目标结点前的结点数=其左子树的结点数;2.目标结点是其根结点的右子结点,那目标结点前的结点数=其左子树的结点数+其根结点的左子树的结点数。
最坏情况下,OS-RANK运行时间与树高成正比,所以还是为O (lgn)。

8.扩展数据结构有什么步骤?

  1. 选择基础数据结构;
  2. 确定要在基础数据结构种添加哪些信息;
  3. 验证可用基础数据结构的基本操作来维护这些新添加的信息;
  4. 设计新的操作。

9.为什么一般hash表的桶数会取一个素数

设有一个哈希函数H( c ) = c % N;
当N取一个合数时,最简单的例子是取2^n,比如说取2^3=8,这时候
H( 11100(二进制) ) = H( 28 ) = 4
H( 10100(二进制) ) = H( 20 )= 4

这时候c的二进制第4位(从右向左数)就”失效”了,也就是说,无论第c的4位取什么值,都会导致H( c )的值一样,这样H( c )就无法完整地反映c的特性,增大了导致冲突的几率。取其他合数时,都会不同程度的导致c的某些位”失效”,从而在一些常见应用中导致冲突。
但是取质数,基本可以保证c的每一位都参与H( c )的运算,从而在常见应用中减小冲突几率。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值