红黑树的概念
1.1.红黑树是什么
红黑树是一种二叉搜索树,它的每个节点增加一个存储代表着该节点的颜色,和它的名字一样,节点的颜色可以是红色或者是黑色。通过对任何一条根到叶子的路径上各个节点的颜色进行约束,红黑树确保没有一条路径会比其他路径长出 2 倍,因而是接近平衡的。
红黑树是被很多条规则进行束缚的二叉搜索树,通过这些规则,可以保证红黑树没有一条路径会比其他路径长出 2 倍,并且保持其相对平衡,下面我来讲述一下这些规则。
1.2.红黑树的规则
- 每个节点不是黑色的就是红色的(这肯定,不然不会被叫做红黑树了)。
- 根节点必须是黑色的。
- 如果一个节点是红色的,则它的两个孩子节点必须是黑色的,也就是说任意一条路径上并不会出现连续的红色的节点。
- 对于任意的一个节点,从该节点到其所有的 NULL 节点的简单路径上,均包含着相同数目的黑色节点。
以上便就是红黑树的四条规则,其实《算法导论》上也是补充了一个知识点:每个叶子节点(NULL)都是黑色的规则。他这里所指的叶子节点不是传统意义上的叶子节点,而是我们所说的空节点,有些书籍上也把 NULL 叫做外部节点。NULL 是为了方便准确的标志处所有路径,《算法导论》在后续实现的细节中忽略了 NULL 节点,所以我们知道这个概念即可。
1.3.红黑树如何确保最长路径不超过最短路径的两倍
由规则 4 可以知道,每条路径上有着数量相同的黑色节点,所以在极端场景下,就比如上面最后一个图,一条路上全都是黑色节点,最短路径其实就是一条路上全是黑色节点,我们将最短路径的长度记作 bh。
在看规则 2 和规则三,我们可以清楚的知道,一条路上不会出现连续的红色节点,所以我们容易知道最长路径应该是一红一黑交织组成,所以最长路径的长度是 2bh。
根据红黑树的规则,我们可以知道一般情况下的红黑树的路径是由数量不等的红黑节点组成的,一红一黑交织进行和全是黑的是不太容易出现的,所以假设红黑树一条路径的长度为 h,所以 bh<=h<=2bh,即红黑树的最长路径不会是最短路径的两倍。
1.4.红黑树的效率问题
我们假设红黑树的节点个数为 N,最短路径的长度为 h,所以可知:2^h - 1 <= N < 2^2*h - 1,由此可以推出 h 大约等于 logN,也就是红黑树查找的效率问题最差也是 2 * logN,时间复杂度终归还是 O(log(N))。
红黑树的表达相对 AVL 树是要更抽象一些的,AVL 树可以根据高度更加直观的看出树的平衡,而红黑树则是需要根据四条规则的颜色进行约束,间接的实现近似平衡,他们的效率都是同一档次的,但是相对而言,插入相同数量的节点,红黑树的旋转次数变的更少了,因为它对平衡的把控并不是那么的严格。
红黑树的实现
2.1.红黑树的结构
在我们书写平衡树的代码之前,通常我们需要先完善一下它的结构,就比如其中最基础的,它的每个节点应该存储什么的数据,红黑树也是一个典型的三叉链,所以它的节点需要包含它的父节点以及左右孩子节点,并且由于红黑树是通过颜色进行平衡的位置,我们还需要设置好一个联合体来确定节点的颜色,我们默认颜色就是红色,并且红黑树也是通过 Key 和 Value 进行存放数据的,Key 就好比我们开车进入商场停车场的车牌号,而 Value 就是记录进入停车场的时间的,由此来确定需要缴纳多少钱。所以由此来看 Key 就是一个标记,而红黑树真正的核心应该是 Value。下面我来展示一下红黑树结构相关的代码。
enum Colour { RED, BLACK }; // 这里我们默认按 key/value 结构实现
template<class K, class V> struct RBTreeNode
{
pair<K, V> _kv;
RBTreeNode<K, V>* _left;
RBTreeNode<K, V>* _right;
RBTreeNode<K, V>* _parent;
Colour _col; // 存放节点的颜色
RBTreeNode(const pair<K, V>& kv) :_kv(kv) ,_left() ,_right() ,_parent() ,_col(RED) {}
};
< , >
{
RBTreeNode<K,V> Node;
:
:
Node* _root = ;
};


