目录

(1)红黑树有以下五点性质:
(2)红黑树如何确保从根到叶子的最长可能路径不会超过最短可能路径的两倍?
- template<class K, class V>
- struct RBTreeNode
- {
- //三叉链
- RBTreeNode
* _left; - RBTreeNode
* _right; - RBTreeNode
* _parent; -
- //存储的键值对
- pair
_kv; -
- //结点的颜色
- int _col; //红/黑
-
- //构造函数
- RBTreeNode(const pair
& kv) - :_left(nullptr)
- , _right(nullptr)
- , _parent(nullptr)
- , _kv(kv)
- , _col(RED)
- {}
- };
- //枚举定义结点的颜色
- enum Colour
- {
- RED,
- BLACK
- };
为什么构造结点时,默认将结点的颜色设置为红色?
红黑树插入结点的逻辑分为三步:
其中前两步与二叉搜索树插入结点时的逻辑相同,红黑树的关键在于第三步对红黑树的调整。
约定:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点
注意:此处所看到的树可能是一颗完整的树,也可能是一颗子树
红黑树在插入结点后是如何调整
(1)叔叔存在且为红 ,cur为红,p为红,g为黑

(2) 叔叔存在且为黑/不存在,cur为红,p为红,g为黑
说明: u的情况有两种
- 1.如果u节点不存在,则cur一定是新插入节点,因为如果cur不是新插入节点,则cur和p一定有一个节点的颜色是黑色,就不满足性质4:每条路径黑色节点个数相同。
- ⒉.如果u节点存在,则其一定是黑色的,那么cur节点原来的颜色一定是黑色的,现在看到其是红色的原因是因为cur的子树在调整的过程中将cur节点的颜色由黑色改成红色。


(3)叔叔存在且为黑/不存在,cur为红,p为红,g为黑

(4)代码
- //插入函数
- pair
bool > Insert(const pair& kv) - {
- if (_root == nullptr) //若红黑树为空树,则插入结点直接作为根结点
- {
- _root = new Node(kv);
- _root->_col = BLACK; //根结点必须是黑色
- return make_pair(_root, true); //插入成功
- }
-
- //1、按二叉搜索树的插入方法,找到待插入位置
- Node* cur = _root;
- Node* parent = nullptr;
- while (cur)
- {
- if (kv.first < cur->_kv.first) //待插入结点的key值小于当前结点的key值
- {
- //往该结点的左子树走
- parent = cur;
- cur = cur->_left;
- }
- else if (kv.first > cur->_kv.first) //待插入结点的key值大于当前结点的key值
- {
- //往该结点的右子树走
- parent = cur;
- cur = cur->_right;
- }
- else //待插入结点的key值等于当前结点的key值
- {
- return make_pair(cur, false); //插入失败
- }
- }
-
- //2、将待插入结点插入到树中
- cur = new Node(kv); //根据所给值构造一个结点
- Node* newnode = cur; //记录新插入的结点(便于后序返回)
- if (kv.first < parent->_kv.first) //新结点的key值小于parent的key值
- {
- //插入到parent的左边
- parent->_left = cur;
- cur->_parent = parent;
- }
- else //新结点的key值大于parent的key值
- {
- //插入到parent的右边
- parent->_right = cur;
- cur->_parent = parent;
- }
-
- //3、若插入结点的父结点是红色的,则需要对红黑树进行调整
- while (parent&&parent->_col == RED)
- {
- Node* grandfather = parent->_parent; //parent是红色,则其父结点一定存在
- if (parent == grandfather->_left) //parent是grandfather的左孩子
- {
- Node* uncle = grandfather->_right; //uncle是grandfather的右孩子
- if (uncle&&uncle->_col == RED) //情况1:uncle存在且为红
- {
- //颜色调整
- parent->_col = uncle->_col = BLACK;
- grandfather->_col = RED;
-
- //继续往上处理
- cur = grandfather;
- parent = cur->_parent;
- }
- else //情况2+情况3:uncle不存在 + uncle存在且为黑
- {
- if (cur == parent->_left)
- {
- RotateR(grandfather); //右单旋
-
- //颜色调整
- grandfather->_col = RED;
- parent->_col = BLACK;
- }
- else //cur == parent->_right
- {
- RotateLR(grandfather); //左右双旋
-
- //颜色调整
- grandfather->_col = RED;
- cur->_col = BLACK;
- }
- break; //子树旋转后,该子树的根变成了黑色,无需继续往上进行处理
- }
- }
- else //parent是grandfather的右孩子
- {
- Node* uncle = grandfather->_left; //uncle是grandfather的左孩子
- if (uncle&&uncle->_col == RED) //情况1:uncle存在且为红
- {
- //颜色调整
- uncle->_col = parent->_col = BLACK;
- grandfather->_col = RED;
-
- //继续往上处理
- cur = grandfather;
- parent = cur->_parent;
- }
- else //情况2+情况3:uncle不存在 + uncle存在且为黑
- {
- if (cur == parent->_left)
- {
- RotateRL(grandfather); //右左双旋
-
- //颜色调整
- cur->_col = BLACK;
- grandfather->_col = RED;
- }
- else //cur == parent->_right
- {
- RotateL(grandfather); //左单旋
-
- //颜色调整
- grandfather->_col = RED;
- parent->_col = BLACK;
- }
- break; //子树旋转后,该子树的根变成了黑色,无需继续往上进行处理
- }
- }
- }
- _root->_col = BLACK; //根结点的颜色为黑色(可能被情况一变成了红色,需要变回黑色)
- return make_pair(newnode, true); //插入成功
- }
-
-
-
- //左单旋
- void RotateL(Node* parent)
- {
- Node* subR = parent->_right;
- Node* subRL = subR->_left;
-
-
- //1、建立parent和subRL之间的关系
- parent->_right = subRL;
- if (subRL) //subRL可能为空
- subRL->_parent = parent;
-
- //2.记录pparent
- Node* pparent = parent->_parent;
-
- //3.建立parent和subR的关系
- subR->_left = parent;
- parent->_parent = subR;
-
- //4.建立pparent和subR的关系
- if (pparent == nullptr) //parent为根,是一颗单独的树
- {
- _root = subR;
- subR->_parent = nullptr; //subR的_parent指向需改变
- }
- else
- {
- if (parent == pparent->_left)
- {
- pparent->_left = subR;
- }
- else //parent == pparent->_right
- {
- pparent->_right = subR;
- }
- subR->_parent = pparent;
- }
-
- }
-
- //右单旋
- void RotateR(Node* parent)
- {
-
-
- Node* subL = parent->_left;
- Node* subLR = subL->_right;
-
- //1.建立parent和subLR的关系
- parent->_left = subLR;
- if (subLR) //subLR可能为空
- {
- subLR->_parent = parent;
- }
-
- //2.记录pparent
- Node* pparent = parent->_parent;
-
- //3.建立subL和parent的关系
- subL->_right = parent;
- parent->_parent = subL;
-
-
- //4.建立pparen和subL的关系
- if (parent == _root) //parent是一颗独立的树
- {
- _root = subL;
- _root->_parent = nullptr;
- }
- else //parent是一颗子树,
- {
- if (pparent->_left == parent)
- {
- pparent->_left = subL;
- }
- else
- {
- pparent->_right = subL;
- }
- subL->parent = pparent;
- }
-
- }
-
- //左右双旋
- void RotateLR(Node* parent)
- {
- RotateL(parent->_left);
- RotateR(parent);
- }
-
- //右左双旋
- void RotateRL(Node* parent)
- {
- RotateR(parent->_right);
- RotateL(parent);
- }
(1)验证二叉树
- //中序遍历
- void Inorder()
- {
- _Inorder(_root);
- }
-
- //中序遍历子函数
- void _Inorder(Node* root)
- {
- if (root == nullptr)
- return;
- _Inorder(root->_left);
- cout << root->_kv.first << " ";
- _Inorder(root->_right);
- }
(2)验证红黑树,是否满足那几个性质
- //判断是否为红黑树
- bool ISRBTree()
- {
- if (_root == nullptr) //空树是红黑树
- {
- return true;
- }
-
- if (_root->_col == RED)
- {
- cout << "error:根结点为红色" << endl;
- return false;
- }
-
- //找最左路径作为黑色结点数目的参考值
- Node* cur = _root;
- int BlackCount = 0;
- while (cur)
- {
- if (cur->_col == BLACK)
- BlackCount++;
-
- cur = cur->_left;
- }
-
- int count = 0;
- return _ISRBTree(_root, count, BlackCount);
- }
-
- //判断是否为红黑树的子函数
- bool _ISRBTree(Node* root, int count, int BlackCount)
- {
- if (root == nullptr) //该路径已经走完了
- {
- if (count != BlackCount)
- {
- cout << "error:黑色结点的数目不相等" << endl;
- return false;
- }
- return true;
- }
-
- if (root->_col == RED&&root->_parent->_col == RED)
- {
- cout << "error:存在连续的红色结点" << endl;
- return false;
- }
-
- if (root->_col == BLACK)
- {
- count++;
- }
- return _ISRBTree(root->_left, count, BlackCount)
- && _ISRBTree(root->_right, count, BlackCount);
- }
- //查找函数
- Node* Find(const K& key)
- {
- Node* cur = _root;
- while (cur)
- {
- if (key < cur->_kv.first) //key值小于该结点的值
- {
- cur = cur->_left; //在该结点的左子树当中查找
- }
- else if (key > cur->_kv.first) //key值大于该结点的值
- {
- cur = cur->_right; //在该结点的右子树当中查找
- }
- else //找到了目标结点
- {
- return cur; //返回该结点
- }
- }
- return nullptr; //查找失败
- }
红黑树和AVL树都是高效的平衡二叉树,增删查改的时间复杂度都是O(logN),但红黑树和AVL树控制二叉树平衡的方式不同:
相对于AVL树来说,红黑树降低了插入结点时需要进行的旋转的次数,所以在经常进行增删的结构中性能比AVL树更优,实际运用时也大多用的是红黑树。