『 C++ 入门到放弃 』- 红黑树
1. 红黑树的概念
红⿊树是⼀棵⼆叉搜索树,他的每个结点增加⼀个数据来表⽰结点的颜⾊,可以是红⾊或者⿊⾊。 通过对任何⼀条从根到叶⼦的路径上各个结点的颜⾊进⾏约束,红⿊树确保没有⼀条路径会⽐其他路径⻓出2倍,因⽽是接近平衡的
红黑树有以下几条规则
- 每个结点不是红⾊就是⿊⾊
- 根结点是⿊⾊的
- 如果⼀个结点是红⾊的,则它的两个孩⼦结点必须是⿊⾊的,也就是说任意⼀条路径不会有连续的
红⾊结点- 对于任意⼀个结点,从该结点到其所有NULL结点的简单路径上,均包含相同数量的⿊⾊结点
2. 红黑树的实现
2.1 基本结构
// 枚举 => 用来表示一个节点是红还是黑 enum Color{RED,BLACK }; // Node的结构 ( 以key/value 做演示 ) template<class K,class V>struct RBTNode{RBTNode(const K& key,const V& value):_left(nullptr),_right(nullptr),_parent(nullptr),_key(key),_value(value),_color(RED){}RBTNode<K,V>* _left;RBTNode<K,V>* _right;RBTNode<K,V>* _parent;K _key;V _value;Color _color;}; // 树的结构 template <class K, class V> class RBT {typedef RBTNode<K, V> Node;private:Node *_root = nullptr; };
2.2 插入
红黑树的插入也需要针对不同情况作处理 => 插入一个新节点后不能破坏红黑树的性质
如果是空树插⼊,新增结点是⿊⾊结点;如果是⾮空树插⼊,新增结点必须红⾊结点,因为⾮空树插⼊,新增⿊⾊结点就破坏了规则4,规则4是很难维护的。
⾮空树插⼊后,新增结点必须红⾊结点,如果⽗亲结点是⿊⾊的,则没有违反任何规则,插⼊结束
⾮空树插⼊后,新增结点必须红⾊结点,如果⽗亲结点是红⾊的,则违反规则3 => 此时需要根据不同情况作处理
(1) 变色、不旋转 => 连续红节点且叔叔节点为红节点
旋转的特点为叔叔节点是黑节点
(2) 变色、单旋
//右旋逻辑 => 和AVL树是一样的,只是不用调整平衡因子 void RotateR(Node *parent) { /*右旋的目标:1. 将parent的左子节点subL提升为新的根节点2. 将subL的右子节点subLR接到parent的左子节点位置3. 将parent接到subL的右子节点位置4. 更新所有相关的父节点指针,维护树的完整性*/Node *subL = parent->_left; // 获取左子节点(将成为新的根节点)Node *subLR = subL->_right; // 获取左子节点的右子节点(需要重新接)// 步骤1:将subLR接到parent的左子节点位置parent->_left = subLR;if (subLR) {subLR->_parent = parent; // 更新subLR的父节点指针}// 步骤2:将parent接到subL的右子节点位置subL->_right = parent;// 步骤3:更新祖父节点的指向关系Node *parentParent = parent->_parent; // 获取祖父节点parent->_parent = subL; // 更新parent的父节点指针if (parentParent == nullptr) {// 情况A:parent是根节点,subL成为新的根节点_root = subL;subL->_parent = nullptr; // 根节点没有父节点} else {// 情况B:parent不是根节点,需要更新祖父节点的指向if (parent == parentParent->_left) {parentParent->_left = subL; // parent原本是左子节点,subL也成为左子节点} else {parentParent->_right = subL; // parent原本是右子节点,subL也成为右子节点}subL->_parent = parentParent; // 更新subL的父节点指针} }
// 左旋逻辑 void RotateL(Node *parent) {/*左旋的目标:1. 将parent的右子节点subR提升为新的根节点2. 将subR的左子节点subRL接到parent的右子节点位置3. 将parent接到subR的左子节点位置4. 更新所有相关的父节点指针,维护树的完整性*/Node *subR = parent->_right; // 获取右子节点(将成为新的根节点)Node *subRL = subR->_left; // 获取右子节点的左子节点(需要重新接)// 步骤1:将subRL接到parent的右子节点位置parent->_right = subRL;if (subRL) {subRL->_parent = parent; // 更新subRL的父节点指针}// 步骤2:将parent接到subR的左子节点位置subR->_left = parent;// 步骤3:更新祖父节点的指向关系Node *parentParent = parent->_parent; // 获取祖父节点parent->_parent = subR; // 更新parent的父节点指针if (parentParent == nullptr) {// 情况A:parent是根节点,subR成为新的根节点_root = subR;subR->_parent = nullptr; // 根节点没有父节点} else {// 情况B:parent不是根节点,需要更新祖父节点的指向if (parent == parentParent->_right) {parentParent->_right = subR; // parent原本是右子节点,旋转后subR成为新的右子节点} else {parentParent->_left = subR; // parent原本是左子节点,旋转后subR成为新的左子节点}subR->_parent = parentParent; // 更新subR的父节点指针} }
(3) 双旋、变色
bool insert(const pair<K, V> &kv) {// 1. 如果根节点为空,直接插入为根节点,并设为黑色if (_root == nullptr) {_root = new Node(kv);_root->_color = BLACK;return true;}// 2. 根节点不为空,寻找插入位置Node *parent = nullptr;Node *cur = _root;while (cur) {// 如果当前节点的key小于插入key,往右子树找if (cur->_kv.first < kv.first) {parent = cur;cur = cur->_right;} else if (cur->_kv.first >kv.first) { // 如果当前节点的key大于插入key,往左子树找parent = cur;cur = cur->_left;} else { // 如果已经存在相同key,不插入return false;}}// 2.2 插入新节点到找到的位置cur = new Node(kv);if (parent->_kv.first < kv.first) {parent->_right = cur;} else {parent->_left = cur;}cur->_parent = parent;// 2.3 插入后进行红黑树性质调整while (parent && parent->_color == RED) {Node *grandfather = parent->_parent; // 祖父节点// 情况1:父节点是祖父的左孩子if (grandfather->_left == parent) {Node *uncle = grandfather->_right; // 叔叔节点// 1.1 叔叔存在且为红色,进行颜色翻转if (uncle && uncle->_color == RED) {parent->_color = BLACK;uncle->_color = BLACK;grandfather->_color = RED;// 继续向上调整cur = grandfather;parent = cur->_parent;}// 1.2 叔叔不存在或为黑色,需要旋转else {// 当前节点是父节点的左孩子,右旋祖父if (cur == parent->_left) {RotateR(grandfather);parent->_color = BLACK;grandfather->_color = RED;} else { // 当前节点是父节点的右孩子,先左旋父节点再右旋祖父RotateL(parent);RotateR(grandfather);parent->_color = BLACK;grandfather->_color = RED;}break; // 调整完毕,跳出循环}// 情况2:叔叔不存在或为黑} else {Node *uncle = grandfather->_left; // 叔叔节点// 2.1 叔叔存在且为红if (uncle && uncle->_color == RED) {parent->_color = BLACK;uncle->_color = BLACK;grandfather->_color = RED;// 继续向上调整cur = grandfather;parent = cur->_parent;}// 2.2 叔叔不存在或为黑else {// 当前节点是父节点的右孩子,左旋祖父if (cur == parent->_right) {RotateL(grandfather);parent->_color = BLACK;grandfather->_color = RED;} else { // 当前节点是父节点的左孩子,先右旋父节点再左旋祖父RotateR(parent);RotateL(grandfather);parent->_color = BLACK;grandfather->_color = RED;}break; // 调整完毕,跳出循环}}}// 最后确保根节点为黑色_root->_color = BLACK;return true; }
2.3 查找
Node *find(const K &key) {Node *cur = _root;while (cur) {if (cur->_kv.first < key) {cur = cur->_right;} else if (cur->_kv.first > key) {cur = cur->_left;} else {return cur;}}return nullptr; }
2.4 判断红黑树是否平衡
// 检查红黑树是否满足所有性质// root: 当前检查的节点// blackNum: 当前路径经过的黑色节点数// refNum: 标准黑色节点数(根到最左叶子的黑色节点数)// 返回true表示满足红黑树性质bool Check(Node *root, int blackNum, const int refNum) {if (root == nullptr) {// 遍历到空节点,检查黑色节点数是否一致if (refNum != blackNum) {cout << "存在黑色结点的数量不相等的路径" << endl;return false;}return true;}// 检查是否有连续红色节点if (root->_color == RED && root->_parent->_color == RED) {cout << root->_kv.first << "存在连续的红色结点" << endl;return false;}if (root->_color == BLACK) {blackNum++;}return Check(root->_left, blackNum, refNum) &&Check(root->_right, blackNum, refNum);}// 判断红黑树是否平衡(即是否满足红黑树所有性质)bool IsBalance() {if (_root == nullptr) {return true;}if (_root->_color == RED) {cout << "根节点是红色" << endl;return false;}int refNum = 0;Node *left = _root;// 统计根到最左叶子的黑色节点数while (left) {if (left->_color == BLACK) {refNum++;}left = left->_left;}return Check(_root, 0, refNum);}
3. AVL树 vs. 红黑树
与红黑树相比,AVL 树更加平衡,但在插入和删除操作中可能需要更多旋转。因此,如果应用程序涉及频繁的插入和删除操作,则应优先选择红黑树。如果插入和删除操作较少,而搜索操作较为频繁,则应优先选择AVL 树而非红黑树。
此外,AVL树性质被破坏时一定会旋转,而红黑树不一定 ( 可能只是变色 )
4. set 和 map 的实现
set 和 map 的实现底层是用红黑树实现的,因此在这里也模拟实现
再模拟实现 set 和 map 前要先修改一下我们前面写的红黑树
enum Color { RED, BLACK };// 红黑树节点结构体模板 // T:节点存储的数据类型(如 pair<K, V> 或自定义 struct) // 设计原因:模板化节点类型,支持多种数据结构 template <class T> struct RBTNode {// 构造函数,初始化数据并默认颜色为红色RBTNode(const T &data): _left(nullptr), _right(nullptr), _parent(nullptr), _data(data),_color(RED) {}RBTNode<T> *_left; RBTNode<T> *_right; RBTNode<T> *_parent; T _data; // 节点存储的数据类型 Color _color; };// 红黑树迭代器模板 // T:节点数据类型,Ref/Ptr:引用和指针类型 template <class T, class Ref, class Ptr> struct RBTreeIterator {typedef RBTNode<T> Node;typedef RBTreeIterator<T, Ref, Ptr> Self;Node *_node; // 当前迭代器指向的节点Node *_root; // 整棵树的根节点(用于--到end时定位最大节点)RBTreeIterator(Node *node, Node *root) : _node(node), _root(root) {}Ref operator*() { return _node->_data; } // 解引用,返回节点数据Ptr operator->() { return &_node->_data; } // 指针访问// 前置++,移动到下一个节点(中序遍历)Self &operator++() {if (_node->_right) {Node *leftmost = _node->_right;while (leftmost->_left)leftmost = leftmost->_left;_node = leftmost;} else {Node *cur = _node, *parent = cur->_parent;while (parent && cur == parent->_right) {cur = parent;parent = parent->_parent;}_node = parent;}return *this;}// 前置--,移动到上一个节点(中序遍历)Self &operator--() {if (_node == nullptr) { // 如果为空 => 找最右边的节点Node *rightmost = _root;while (rightmost->_right)rightmost = rightmost->_right;_node = rightmost;// 如果左子树存在 => 找左子树的最右的节点} else if (_node->_left) {Node *rightmost = _node->_left;while (rightmost->_right)rightmost = rightmost->_right;_node = rightmost;} else {Node *cur = _node, *parent = cur->_parent;while (parent && cur == parent->_left) {cur = parent;parent = parent->_parent;}_node = parent;}return *this;}bool operator!=(const Self &s) const { return _node != s._node; }bool operator==(const Self &s) const { return _node == s._node; }};// 红黑树主类模板 // K:键类型,T:节点数据类型,KeyOfT:仿函数,从T中提取K // 设计原因:支持泛型键值类型,KeyOfT适配不同数据结构 template <class K, class T, class KeyOfT> class RBT {typedef RBTNode<T> Node;public:typedef RBTreeIterator<T, T &, T *> Iterator;typedef RBTreeIterator<T, const T &, const T *> ConstIterator;RBT() = default;// 返回最左节点的迭代器(begin)Iterator begin() {Node *leftmost = _root;while (leftmost && leftmost->_left)leftmost = leftmost->_left;return Iterator(leftmost, _root);}// 返回end迭代器(空节点)Iterator end() { return Iterator(nullptr, _root); }ConstIterator begin() const {Node *leftmost = _root;while (leftmost && leftmost->_left)leftmost = leftmost->_left;return ConstIterator(leftmost, _root);}ConstIterator end() const { return ConstIterator(nullptr, _root); }// 析构函数,递归释放所有节点~RBT() {Destroy(_root);_root = nullptr;}// 插入数据,返回插入位置和是否插入成功// 设计思路:先查找插入位置,若已存在则返回已存在节点,否则插入并调整红黑树pair<Iterator, bool> insert(const T &data) {if (_root == nullptr) {_root = new Node(data);_root->_color = BLACK;return make_pair(Iterator(_root, _root), true);}KeyOfT kot;Node *parent = nullptr, *cur = _root;while (cur) {if (kot(cur->_data) < kot(data)) {parent = cur;cur = cur->_right;} else if (kot(cur->_data) > kot(data)) {parent = cur;cur = cur->_left;} else {// 已存在return make_pair(Iterator(cur, _root), false);}}cur = new Node(data);if (kot(parent->_data) < kot(data))parent->_right = cur;elseparent->_left = cur;cur->_parent = parent;// 插入后调整红黑树性质while (parent && parent->_color == RED) {Node *grandfather = parent->_parent;if (grandfather->_left == parent) {Node *uncle = grandfather->_right;if (uncle && uncle->_color == RED) {parent->_color = BLACK;uncle->_color = BLACK;grandfather->_color = RED;cur = grandfather;parent = cur->_parent;} else {if (cur == parent->_left) {RotateR(grandfather);parent->_color = BLACK;grandfather->_color = RED;} else {RotateL(parent);RotateR(grandfather);parent->_color = BLACK;grandfather->_color = RED;}break;}} else {Node *uncle = grandfather->_left;if (uncle && uncle->_color == RED) {parent->_color = BLACK;uncle->_color = BLACK;grandfather->_color = RED;cur = grandfather;parent = cur->_parent;} else {if (cur == parent->_right) {RotateL(grandfather);parent->_color = BLACK;grandfather->_color = RED;} else {RotateR(parent);RotateL(grandfather);parent->_color = BLACK;grandfather->_color = RED;}break;}}}_root->_color = BLACK;return make_pair(Iterator(cur, _root), true);}// 查找数据,返回迭代器Iterator find(const T &data) {KeyOfT kot;Node *cur = _root;while (cur) {if (kot(cur->_data) < kot(data))cur = cur->_right;else if (kot(cur->_data) > kot(data))cur = cur->_left;elsereturn Iterator(cur, _root);}return end();}// 检查红黑树是否满足所有性质bool Check(Node *root, int blackNum, const int refNum) {if (root == nullptr) {if (refNum != blackNum) {cout << "存在黑色节点数量不相等的路径" << endl;return false;}return true;}if (root->_color == RED && root->_parent && root->_parent->_color == RED) {cout << "存在连续的红色节点" << endl;return false;}if (root->_color == BLACK)blackNum++;return Check(root->_left, blackNum, refNum) &&Check(root->_right, blackNum, refNum);}// 判断红黑树是否平衡bool IsBalance() {if (_root == nullptr)return true;if (_root->_color == RED) {cout << "根节点是红色" << endl;return false;}int refNum = 0;Node *left = _root;while (left) {if (left->_color == BLACK)refNum++;left = left->_left;}return Check(_root, 0, refNum);}private:// 递归销毁所有节点void Destroy(Node *root) {if (root == nullptr)return;Destroy(root->_left);Destroy(root->_right);delete root;}Node *_root = nullptr; // 红黑树根节点// 右旋操作,维护红黑树平衡void RotateR(Node *parent) {Node *subL = parent->_left;Node *subLR = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;subL->_right = parent;Node *parentParent = parent->_parent;parent->_parent = subL;if (parentParent == nullptr) {_root = subL;subL->_parent = nullptr;} else {if (parent == parentParent->_left)parentParent->_left = subL;elseparentParent->_right = subL;subL->_parent = parentParent;}}// 左旋操作,维护红黑树平衡void RotateL(Node *parent) {Node *subR = parent->_right;Node *subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;subR->_left = parent;Node *parentParent = parent->_parent;parent->_parent = subR;if (parentParent == nullptr) {_root = subR;subR->_parent = nullptr;} else {if (parent == parentParent->_right)parentParent->_right = subR;elseparentParent->_left = subR;subR->_parent = parentParent;}} };
// myset.h // 要include 红黑树的代码,再复用 template <class K> class set {// 仿函数,直接返回key本身// 设计原因:set的元素就是key,不需要从pair中提取dstruct SetKeyOfT {const K &operator()(const K &key) { return key; }};public:// 迭代器类型定义,直接复用RBT的迭代器typedef typename RBT<K, K, SetKeyOfT>::Iterator iterator;typedef typename RBT<K, K, SetKeyOfT>::ConstIterator const_iterator;// begin/end接口,支持范围for遍历iterator begin() { return _t.begin(); }iterator end() { return _t.end(); }const_iterator begin() const { return _t.begin(); }const_iterator end() const { return _t.end(); }// 插入元素,返回插入结果pair<iterator, bool> insert(const K &key) { return _t.insert(key); }// 查找元素,返回迭代器iterator find(const K &key) { return _t.find(key); }private:RBT<K, K, SetKeyOfT> _t; // 底层用红黑树实现 };
// mymap.h// map模板类,K为key类型,V为value类型 template <class K, class V> class map {// 仿函数,从pair<K, V>中提取keystruct MapKeyOfT {const K &operator()(const pair<K, V> &kv) { return kv.first; }};public:// 迭代器类型定义,复用RBT的迭代器typedef typename RBT<K, pair<const K, V>, MapKeyOfT>::Iterator iterator;typedef typename RBT<K, pair<const K, V>, MapKeyOfT>::ConstIteratorconst_iterator;iterator begin() { return _t.begin(); }iterator end() { return _t.end(); }const_iterator begin() const { return _t.begin(); }const_iterator end() const { return _t.end(); }// 下标操作符,若key不存在则插入默认值V &operator[](const K &key) {pair<iterator, bool> ret = insert(make_pair(key, V()));return ret.first->second;}// 插入键值对pair<iterator, bool> insert(const pair<K, V> &kv) { return _t.insert(kv); }// 查找key,返回迭代器iterator find(const K &key) { return _t.find(key); }private:RBT<K, pair<K, V>, MapKeyOfT> _t; // 底层用红黑树实现 };