1.红黑树的概念
红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或
Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路
径会比其他路径长出俩倍,因而是接近平衡的。
2.红黑树的性质
1. 每个结点不是红色就是黑色
2. 根节点是黑色的
3. 如果一个节点是红色的,则它的两个孩子结点是黑色的 (没有连续的红色节点)
4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点
5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)NIF节点
思考:为什么满足上面的性质,红黑树就能保证:其最长路径中节点个数不会超过最短路径节点
个数的两倍?看图
路径:从该节点到NIF节点
最短路径:全黑节点
最长路径:一黑一红
3.红黑树的平衡
节点定义
enum Color
{RED,BLACK,
};template<class K,class V>
class RBNode
{
public:typedef RBNode<K, V> Node;RBNode(const pair<K, V>& kv):_left(nullptr), _right(nullptr), _parent(nullptr), _col(RED), _kv(kv){}Node* _left;Node* _right;Node* _parent;Color _col;pair<K, V> _kv;//库里面提供的结构体,表示key和value};
颜色初始化必须是红色,如果插入的是黑色,必定会影响每条路径的黑色节点的数量,红色的话只会影响该条路径
插入
template<class K,class V>
class RBTree
{
public:typedef RBNode<K, V> Node;bool Insert(const pair<K, V>& kv){if (_root == nullptr){_root = new Node(kv);_root->_col = BLACK;return true;}Node* cur = _root;Node* parent = nullptr;while (cur){if (kv.first < cur->_kv.first){parent = cur;cur = cur->_left;}else if (kv.first > cur->_kv.first){parent = cur;cur = cur->_right;}else{return false;}}cur = new Node(kv);if (parent->_kv.first > kv.first){parent->_left = cur;}else{parent->_right = cur;}cur->_parent = parent;//...........}private:Node* _root = nullptr;
};
插入分为三种情况(p为parent,g为grandparent,u为uncle)
cur一定为红色了
1:p是黑色,不用处理
2:p是红色,u存在且为红色
3:p是红色,u不存在或存在为黑色
情况2:
p是红色,u存在且为红色
变色+向上
情况3:
p是红色,u不存在或存在为黑色
u在右孩子时
u在左孩子同理
下面是插入后面实现的代码,里面有注释
while(parent&&parent->_col == RED){Node* grandparent = parent->_parent;if (grandparent->_right == parent){Node* uncle = grandparent->_left;//情况1:u存在且为红,变色加向上调整if (uncle && uncle->_col == RED){parent->_col = BLACK;uncle->_col = BLACK;grandparent->_col = RED;//向上调整cur = grandparent;parent = cur->_parent;}else//情况2a:u不存在或者u存在且为黑 旋转+变色{/* gu pc*///左单旋+变色if (parent->_right == cur){RotateL(grandparent);grandparent->_col = RED;parent->_col = BLACK;}else{/* gu pc *///右单旋+左单旋+变色RotateR(parent);RotateL(grandparent);cur->_col = BLACK;grandparent->_col = RED;}break;}}else{Node* uncle = grandparent->_right;//情况1:u存在且为红,变色加向上调整if (uncle && uncle->_col == RED){parent->_col = BLACK;uncle->_col = BLACK;grandparent->_col = RED;//向上调整cur = grandparent;parent = cur->_parent;}else//情况2b:u不存在或者u存在且为黑 旋转+变色{/* gp u c *///右单旋+变色if (parent->_right == cur){RotateR(grandparent);grandparent->_col = RED;parent->_col = BLACK;}else{/* gp uc *///左单旋+右单旋+变色RotateL(parent);RotateR(grandparent);cur->_col = BLACK;grandparent->_col = RED;}break;}}}_root->_col = BLACK;//管你变成什么色,根反正最后还是黑色return true;
4.红黑树的验证
验证是红黑树,就要满足他的性质,首先不能出现连续红色节点,其次每条路径的黑色节点的数量要相等
不能出现连续红色节点:不要用cur,然后和cur->孩子来比较,因为会有两个孩子,比较麻烦,可以用cur->parent来比较
每条路径的黑色节点的数量要相等:先算出一条路径的黑色节点的数量,作为基准值,进行比较
bool IsRBTree(){if (_root && _root->_col == RED){cout << "根节点颜色是红色" << endl;return false;}int nummark = 0;//给个黑色节点基准值,然后和它比较Node* cur = _root;while (cur){if(cur->_col == BLACK)nummark++;cur=cur->_left;}return _Check(_root, 0, nummark);}
bool _Check(Node* root, int blacknum, int nummark){if (root == nullptr){if (nummark != blacknum){cout << "某条路径黑色节点的数量不相等" << endl;cout << blacknum << endl;return false;}return true;}if (root->_col == BLACK){blacknum++;}if (root->_col == RED && root->_parent && root->_parent->_col == RED){cout << "具有连续的红色节点" << endl;return false;}return _Check(root->_left, blacknum, nummark)&& _Check(root->_right, blacknum, nummark);}
void testRBTree()
{srand(time(0));const size_t N = 5000000;RBTree<int, int> t;for (size_t i = 0; i < N; ++i){size_t x = rand() + i;t.Insert(make_pair(x, x));}//t.Inorder();cout << t.IsRBTree() << endl;
}