目录
- 1. 每个结点不是红色就是黑色
- 2. 根节点是黑色的
- 3. 如果一个节点是红色的,则它的两个孩子结点是黑色的
- 4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点
- 5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)
- template<class K, class V>
- struct RBTreeNode
- {
- RBTreeNode<K, V>* _left;
- RBTreeNode<K, V>* _right;
- RBTreeNode<K, V>* _parent;
- pair<K, V> _kv;
-
- Colour _col;
-
- RBTreeNode(const pair<K, V>& kv)
- :_kv(kv)
- , _left(nullptr)
- , _right(nullptr)
- , _parent(nullptr)
- , _col(RED)
- {}
- };
旋转其实与AVL树的旋转是一样的,也是分为左右单旋,和左右.右左双旋。

代码:
- void RotateL(Node* parent)
- {
- Node* ppNode = parent->_parent;//先存储父亲结点的父亲结点
- Node* subR = parent->_right;
- Node* subRL = subR->_left;
-
- parent->_right = subRL;
- subR->_left = parent;
- if(subRL) //subRL可能为空
- subRL->_parent = parent;
- parent->_parent = subR;
-
- if (parent == _root) //如果parent是根节点
- {
- _root = subR;
- _root->_parent = nullptr;
- }
- else //如果parent不是根节点
- {
- if (parent == ppNode->_left)//如果parent在其父亲结点左侧
- {
- ppNode->_left = subR;
- }
- else //如果parent在其父亲结点右侧
- {
- ppNode->_right = subR;
- }
- subR->_parent = ppNode;
- }
- }

对应代码
- void RotateR(Node* parent)
- {
- Node* ppNode = parent->_parent;
- Node* subL = parent->_left;
- Node* subLR = subL->_right;
- subL->_right = parent;
- parent->_left = subLR;
-
- if (subLR)
- subLR->_parent = parent;
- parent->_parent = subL;
-
- if (parent == _root)
- {
- _root = subL;
- _root->_parent = nullptr;
- }
- else
- {
- if (ppNode->_left == parent)
- {
- ppNode->_left = subL;
- }
- else
- {
- ppNode->_right = subL;
- }
- subL->_parent = ppNode;
- }
-
- }
要插入一个节点我们是希望他是红色节点还是黑树节点?很显然是红色节点,如果插入的是黑色节点,这会破坏红黑树中任何一条路径中黑色节点的数量相同。对应这种情况我们对红黑树进行调整就变得很麻烦。因此我们希望插入的节点为红色节点。
先简单定义下概念:把要插入的结点定义为cur,父亲结点定义为p,父亲结点的父亲结点定义为g,其叔叔定义为u.
如图:

主要根据叔叔结点分情况讨论
将p,u结点变黑,如果g不是根结点,把其变红,然后再向上调整。
对应代码实现(假设cur为p的左孩子,p为g的左孩子):
- while (parent && parent->_col == RED)
- {
- Node* grandfater = parent->_parent;
- assert(grandfater);
-
- if (grandfater->_left == parent)
- {
- Node* uncle = grandfater->_right;
- if (uncle && uncle->_col == RED) // 叔叔存在且为红
- {
-
- parent->_col = uncle->_col = BLACK;
- grandfater->_col = RED;
- cur = grandfater;
- parent = cur->_parent;
- }
- }
- }
这种情况直接进行右旋就可以。可直接调用上面右旋的代码
- while (parent && parent->_col == RED)
- {
- Node* grandfater = parent->_parent;
- assert(grandfater);
- if (grandfater->_left == parent)
- {
- Node* uncle = grandfater->_right;
- if (uncle && uncle->_col == RED) // 叔叔存在且为红
- {
- parent->_col = uncle->_col = BLACK;
- grandfater->_col = RED;
- cur = grandfater;
- parent = cur->_parent;
- }
- else // 叔叔不存在 或者 叔叔存在且为黑
- {
- if (cur == parent->_left) // 单旋
- {
- RotateR(grandfater);//进行左单旋
- parent->_col = BLACK;
- grandfater->_col = RED;
- }
- break;
- }
- }
- }
如果u存在且为黑呢?如图:

一样的是以g为旋转点进行右旋。
补充:

p为g的左孩子,cur为p的左孩子,则进行右单旋转;
p为g的右孩子,cur为p的右孩子,则进行左单旋转
p、g变色--p变黑,g变红(g不是根节点的情况下)
cur为红,p为红,g为黑,u不存在/u存在且为黑 ,但 1--:p为g的左孩子,cur为p的右孩子,2--:p为g的右孩子,cur为p的左孩子。
先看一种最简单的情况,u不存在,p为g的左孩子,cur为p的右孩子,如图:

对p进行左单旋,也就变成了情况二中的情况,再对g进行右单旋即可,然后再更改颜色。
如果u存在且为黑呢?

思路是一样的,代码实现:
- while (parent && parent->_col == RED)
- {
- Node* grandfater = parent->_parent;
- assert(grandfater);
- if (grandfater->_left == parent)
- {
- Node* uncle = grandfater->_right;
- if (uncle && uncle->_col == RED) // 叔叔存在且为红
- {
- parent->_col = uncle->_col = BLACK;
- grandfater->_col = RED;
- cur = grandfater;
- parent = cur->_parent;
- }
- else // 叔叔不存在 或者 叔叔存在且为黑
- {
- if (cur == parent->_left) // 单旋,parent为grandfather的左,cur为parent的左
- {
- RotateR(grandfater);//进行左单旋
- parent->_col = BLACK;
- grandfater->_col = RED;
- }
- else // 双旋parent为grandfather的左,cur为parent的右
- {
- RotateL(parent);//对parent进行左单旋
- RotateR(grandfater);//对grandfather进行右单旋
- cur->_col = BLACK;
- grandfater->_col = RED;
- }
- break;
- }
- }
- }
以上情况p都为grandfather的左孩子进行演示的。
完整插入代码实现:
- bool Insert(const pair<K, V>& kv)
- {
- if (_root == nullptr)//如果是空树,直接插入
- {
- _root = new Node(kv);
- _root->_col = BLACK;
- return true;
- }
-
- Node* parent = nullptr;
- Node* cur = _root;
- while (cur) //按规则进行比较
- {
- if (cur->_kv.first < kv.first)
- {
- parent = cur;
- cur = cur->_right;
- }
- else if (cur->_kv.first > kv.first)
- {
- parent = cur;
- cur = cur->_left;
- }
- else
- {
- return false;
- }
- }
-
- cur = new Node(kv);
- cur->_col = RED;
- if (parent->_kv.first < kv.first)
- {
- parent->_right = cur;
- }
- else
- {
- parent->_left = cur;
- }
- cur->_parent = parent;
- //*******************************************************************
- //插入节点成功,下面对节点进行调整
-
- while (parent && parent->_col == RED)
- {
- Node* grandfater = parent->_parent;
- assert(grandfater);
-
- if (grandfater->_left == parent) //p为g的左孩子
- {
- Node* uncle = grandfater->_right; //u为g的右孩子
- if (uncle && uncle->_col == RED) // 叔叔存在且为红。情况一
- {
-
- parent->_col = uncle->_col = BLACK;
- grandfater->_col = RED;
- cur = grandfater;
- parent = cur->_parent;
- }
- else // 叔叔不存在 或者 叔叔存在且为黑
- {
- if (cur == parent->_left) // 单旋,p为g的左孩子,cur为p的左孩子
- {
- RotateR(grandfater);
- parent->_col = BLACK;
- grandfater->_col = RED;
- }
- else // 双旋,p为g的左孩子,cur为p的右孩子
- {
- RotateL(parent);
- RotateR(grandfater);
- cur->_col = BLACK;
- grandfater->_col = RED;
- }
-
- break;
- }
- }
- else //(grandfater->_right == parent),p为g的右孩子
- {
- Node* uncle = grandfater->_left;// u为g的左孩子
- if (uncle && uncle->_col == RED)
- {
-
- parent->_col = uncle->_col = BLACK;
- grandfater->_col = RED;
- cur = grandfater;
- parent = cur->_parent;
- }
- else
- {
- if (cur == parent->_right)
- {
-
- RotateL(grandfater);
- parent->_col = BLACK;
- grandfater->_col = RED;
- }
- else
- {
- RotateR(parent);
- RotateL(grandfater);
- cur->_col = BLACK;
- grandfater->_col = RED;
- }
-
- break;
- }
- }
- }
-
- _root->_col = BLACK;//把根节点变为黑色
-
- return true;
- }
红黑树插入后,如何进行调整主要看u(叔叔)节点,以及cur与p节点的关系
1--u存在且为红:将p,u改为黑,g改为红,然后把g当成cur,继续向上调整。
2--cur为红,p为红,g为黑,u不存在/u存在且为黑:p为g的左孩子,cur为p的左孩子,则进行右单旋转;p为g的右孩子,cur为p的右孩子,则进行左单旋转 .p、g变色--p变黑,g变红
3--cur为红,p为红,g为黑,u不存在/u存在且为黑:p为g的左孩子,cur为p的右孩子,则进行左单旋转;p为g的右孩子,cur为p的左孩子,则进行右单旋转 .这样变为了情况2.
如何验证上面的代码生成的是一颗红黑树呢?
这颗红黑树一共有13条路径,需要检测每条路径是否存在连续的红节点,以及每条路径上黑节点的个数是否相同。可以先算出最右边路径上黑节点的数目,然后从根节点递归检测
中序遍历代码:
- void _InOrder(Node* root)
- {
- if (root == nullptr)
- return;
-
- _InOrder(root->_left);
- cout << root->_kv.first << " ";
- _InOrder(root->_right);
- }
检测每一条路径:
- bool IsBalanceTree()
- {
-
- Node* pRoot = _root;
- if (nullptr == pRoot)//空树也是红黑树
- return true;
-
- if (BLACK != pRoot->_col)//检查根节点
- {
- cout << "违反红黑树性质二:根节点必须为黑色" << endl;
- return false;
- }
-
- size_t blackCount = 0; // 获取任意一条路径中黑色节点的个数 -- 比较基准值
- Node* pCur = pRoot;
- while (pCur)
- {
- if (BLACK == pCur->_col)
- blackCount++;
-
- pCur = pCur->_right;
- }
-
- // 检测是否满足红黑树的性质,k用来记录路径中黑色节点的个数
- size_t k = 0;
- return _IsValidRBTree(pRoot, k, blackCount);
- }
-
-
- bool _IsValidRBTree(Node* pRoot, size_t k, const size_t blackCount)
- {
-
- if (nullptr == pRoot)//走到null之后,判断k和black是否相等
- {
- if (k != blackCount)
- {
- cout << "违反性质四:每条路径中黑色节点的个数必须相同" << endl;
- return false;
- }
- return true;
- }
-
- // 统计黑色节点的个数
- if (BLACK == pRoot->_col)
- k++;
-
- // 检测当前节点与其双亲是否都为红色
- if (RED == pRoot->_col && pRoot->_parent && pRoot->_parent->_col == RED)
- {
- cout << "违反性质三:存在连在一起的红色节点" << endl;
- return false;
- }
-
- return _IsValidRBTree(pRoot->_left, k, blackCount) &&
- _IsValidRBTree(pRoot->_right, k, blackCount);
- }