红黑树
1. 数据结构定义
- 在二叉查找树平衡的情况下,才能保证最坏查找时间为lgN
- 但2-3树要维护两种类型不同的节点,额外开销太大
- 红黑树:红链接 用两个2-节点代替3-节点;黑链接 就是2-3树中的2-节点
2-3树 等价转换成 红黑树
public class RedBlackBST<Key extends Comparable<Key>, Value>{
private Node root; //根节点
private static final boolean RED = true;
private static final boolean BLACK = false;
//private修饰的内部类定义节点类
private class Node{
Key key;
Value value;
Node left,right;
int N; //子树中的节点总数
boolean color;
}
//构造函数 略
}
其中,为什么要用节点计数器N,而不用一个变量来保存整棵树中的节点总数?
答:为了方便实现select和rank方法。如果不需要这两个方法,则可以不用N。
– select方法,选择操作找到排名为k的键(即树中正好有k个小于它的键)
– rank方法,返回给定键key的排名(即树中小于key键的数量)
2.性质
- 性质1. 节点是红色或黑色
- 性质2. 根是黑色
- 性质3. 所有叶子都是黑色
- 性质4. 每个红色节点的两个子节点都是黑色(从每个叶子到根的所有路径上不能有两个连续的红色节点)
- 性质5. 完美黑色平衡:从任一节点到其每个叶子的所有简单路径 都包含相同数目的黑色节点
3.时间复杂度
红黑树的查找相关的实现与标准二叉查找树完全一致,插入、删除实现很复杂。但是查找相关操作比插入、删除使用频率高的多。
所有操作时间都是对数级别
4.对比二叉查找树BST和自平衡二叉查找树AVL
红黑树是牺牲了严格的高度平衡的优越条件为代价,它只要求部分地达到平衡要求,降低了对旋转的要求,从而提高了性能。红黑树能够以O(log2 n)的时间复杂度进行搜索、插入、删除操作。此外,由于它的设计,任何不平衡都会在三次旋转之内解决。当然,还有一些更好的,但实现起来更复杂的数据结构能够做到一步旋转之内达到平衡,但红黑树能够给我们一个比较“便宜”的解决方案。
相比于BST,因为红黑树可以能确保树的最长路径不大于两倍的最短路径的长度,所以可以看出它的查找效果是有最低保证的。在最坏的情况下也可以保证O(logN)的,这是要好于二叉查找树的。因为二叉查找树最坏情况可以让查找达到O(N)。
红黑树的算法时间复杂度和AVL相同,但统计性能比AVL树更高,所以在插入和删除中所做的后期维护操作肯定会比红黑树要耗时好多,但是他们的查找效率都是O(logN),所以红黑树应用还是高于AVL树的. 实际上插入 AVL 树和红黑树的速度取决于你所插入的数据.如果你的数据分布较好,则比较宜于采用 AVL树(例如随机产生系列数),但是如果你想处理比较杂乱的情况,则红黑树是比较快的
5.对比哈希表
权衡四个因素: 查找速度, 数据量, 内存使用,可扩展性。
总体来说,hash查找速度会比map快,而且查找速度基本和数据量大小无关,属于常数级别;而map的查找速度是log(n)级别。并不一定常数就比log(n) 小,hash还有hash函数的耗时,明白了吧,如果你考虑效率,特别是在元素达到一定数量级时,考虑考虑hash。但若你对内存使用特别严格, 希望程序尽可能少消耗内存,那么一定要小心,hash可能会让你陷入尴尬,特别是当你的hash对象特别多时,你就更无法控制了,而且 hash的构造速度较慢。
红黑树并不适应所有应用树的领域。如果数据基本上是静态的,那么让他们待在他们能够插入,并且不影响平衡的地方会具有更好的性能。如果数据完全是静态的,例如,做一个哈希表,性能可能会更好一些。
在实际的系统中,例如,需要使用动态规则的防火墙系统,使用红黑树而不是散列表被实践证明具有更好的伸缩性。Linux内核在管理vm_area_struct时就是采用了红黑树来维护内存块的。
红黑树通过扩展节点域可以在不改变时间复杂度的情况下得到结点的秩。