一. 红黑树源码
虽然 set 参数只有 key,但是介于 map 除了 key 还有 value;我们任然将对一棵KV模型的红黑树进行封装,
//枚举颜色 enum Colour { RED, BLACK }; 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) :_left(nullptr) , _right(nullptr) , _parent(nullptr) , _kv(kv) , _col(RED) {} }; template<class K, class V> struct RBTree { typedef RBTreeNode<K, V> Node; public: //如果是空树,则插入节点作为root节点 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)//待插入结点的key值大于当前结点的key值 { //往节点的右子树走 parent = cur; cur = cur->_right; } else if (kv.first < cur->_kv.first)//待插入结点的key值小于当前结点的key值 { //往节点的左子树走 parent = cur; cur = cur->_left; } else//插入的值等于当前的节点,返回失败 { return false; } } //将节点链接到树上 cur = new Node(kv);//构造节点 cur->_col = RED; if (kv.first < parent->_kv.first) //判断链接左还是右? { //插入到左边 parent->_left = cur; cur->_parent = parent; } else if (kv.first > parent->_kv.first) { //插入到右边 parent->_right = cur; cur->_parent = parent; } //如果插入节点的父节点是红色的,则需要对红黑树进行操作 while (parent && parent->_col == RED) { Node* grandfather = parent->_parent; assert(grandfather); assert(grandfather->_col == BLACK); //关键看叔叔 ~ 判断叔叔的位置 if (parent == grandfather->_left) { Node* uncle = grandfather->_right; //情况1:uncle存在且为红 + 继续往上处理 if (uncle && uncle->_col == RED) { //变色:p和u变黑,g变红 parent->_col = uncle->_col = BLACK; grandfather->_col = RED; //继续往上调整 cur = grandfather; parent = cur->_parent; } else //情况2 + 情况3:uncle不存在 + uncle存在且为黑 { //情况二:单旋 + 变色 // g // p u //c if (cur = parent->_left) { RotateR(grandfather);//右旋 //颜色调整 parent->_col = BLACK; grandfather->_col = RED; } else//cur == parent->_right { //情况三:左右双旋 + 变色 // g // p u // c RotateL(parent); RotateR(grandfather); //调整颜色 cur->_col = BLACK; grandfather->_col = RED; } break; } } else //parent == grandfather->_right { Node* uncle = grandfather->_left; //情况1:uncle存在且为红 + 继续往上处理 if (uncle && uncle->_col == RED) { //变色:p和u变黑,g变红 parent->_col = uncle->_col = BLACK; grandfather->_col = RED; //继续往上调整 cur = grandfather; parent = cur->_parent; } else //情况2 + 情况3:uncle不存在 + uncle存在且为黑 { //情况二:单旋 + 变色 // g // u p // c if (cur = parent->_right) { RotateL(grandfather);//左单 旋 //颜色调整 parent->_col = BLACK; grandfather->_col = RED; } else//cur == parent->_left { //情况三:右左双旋 + 变色 // g // u p // c RotateR(parent); RotateL(grandfather); //调整颜色 cur->_col = BLACK; grandfather->_col = RED; } break; } } } _root->_col = BLACK;//不管什么,最后根要变黑 return true; } void InOrder() { _InOrder(_root); cout << endl; } bool IsBalance() { if (_root == nullptr) { return true; } if (_root->_col == RED) { cout << "根节点不是黑色" << endl; return false; } // 黑色节点数量基准值 int benchmark = 0; Node* cur = _root; while (cur) { if (cur->_col == BLACK) ++benchmark; cur = cur->_left;//以最左的路径进行 } return PrevCheck(_root, 0, benchmark); } private: bool PrevCheck(Node* root, int blackNum, int& benchmark) { if (root == nullptr) { //cout << blackNum << endl; //return; if (benchmark == 0) { benchmark = blackNum; return true; } if (blackNum != benchmark) { cout << "某条黑色节点的数量不相等" << endl; return false; } else { return true; } } if (root->_col == BLACK) { ++blackNum; } //检测它的父亲 if (root->_col == RED && root->_parent->_col == RED) { cout << "存在连续的红色节点" << endl; return false; } return PrevCheck(root->_left, blackNum, benchmark) && PrevCheck(root->_right, blackNum, benchmark); } void _InOrder(Node* root) { if (root == nullptr)//空树也是红黑树 return; _InOrder(root->_left); cout << root->_kv.first << ":" << root->_kv.second << endl; _InOrder(root->_right); } //左单旋 void RotateL(Node* parent) { Node* subR = parent->_right; Node* subRL = subR->_left; Node* parentParent = parent->_parent; //建立subRL与parent之间的联系 parent->_right = subRL; if (subRL) subRL->_parent = parent; //建立parent与subR之间的联系 subR->_left = parent; parent->_parent = subR; //建立subR与parentParent之间的联系 if (parentParent == nullptr) { _root = subR; _root->_parent = nullptr; } else { if (parent == parentParent->_left) { parentParent->_left = subR; } else { parentParent->_right = subR; } subR->_parent = parentParent; } } //右单旋 void RotateR(Node* parent) { Node* subL = parent->_left; Node* subLR = subL->_right; Node* parentParent = parent->_parent; //建立subLR与parent之间的联系 parent->_left = subLR; if (subLR) subLR->_parent = parent; //建立parent与subL之间的联系 subL->_right = parent; parent->_parent = subL; //建立subL与parentParent之间的联系 if (parentParent == nullptr) { _root = subL; _root->_parent = nullptr; } else { if (parent == parentParent->_left) { parentParent->_left = subL; } else { parentParent->_right = subL; } subL->_parent = parentParent; } } //左右双旋 void RotateLR(Node* parent) { RotateL(parent->_left); RotateR(parent); } //右左双旋 void RotateRL(Node* parent) { RotateR(parent->_right); RotateL(parent); } private: Node* _root = nullptr; };
二. 观察源码
🥑底层RBTree的结构
RBTree是根据传的Value的值来判断是什么类型,也就是一棵泛型的RBTree,通过不同的实例化,实现出了Map和Set(传Key就是Set,传pair就是Map)
🥑底层的Key和Map
可见set传的是Key,Map传的是pair
二. 参数的适配
为了实现泛型RBTree,对此我们将红黑树第二个模板参数的名字改为T,让自动识别是map还是set
template<class K, class T>
struct RBTree
T模板参数可能只是键值Key,也可能是由Key和Value共同构成的键值对。如果是set容器,那么它传入底层红黑树的模板参数就是Key和Key:
template<class K> class set { public: //... private: RBTree<K, K> _t; };
但如果是map容器,那么它传入底层红黑树的模板参数就是Key以及Key和Value构成的键值对:
template<class K, class V> class map { public: //... private: RBTree<K, pair<K, V>> _t; };
那么问题来了:如果只看T的判断的话,是不是可以只保留第二个模板参数呢?
1️⃣对于Insert来说确实是这样的,因为此时红黑树的第二个模板参数当中也是有键值Key的,但是Key实际上是不可以省略的!
2️⃣对于set容器来说,省略红黑树的第一个参数当然没问题,因为set传入红黑树的第二个参数与第一个参数是一样的。但是对于map容器来说就不行了,因为map容器所提供的接口当中有些是只要求给出键值Key的,比如find和erase
三. 数据的存储
红黑树的模板参数变成了K和T,那节点存的是什么呢?
看了源码得知
set容器:K和T都代表键值Key
map容器:K代表键值Key,T代表由Key和Value构成的键值对
对于set容器来说,底层红黑树结点当中存储K和T都是一样的,但是对于map容器来说,底层红黑树就只能存储T了。由于底层红黑树并不知道上层容器到底是map还是set,因此红黑树的结点当中直接存储T就行了
这样一来就可以实现泛型树了,当上层容器是set的时候,结点当中存储的是键值Key;当上层容器是map的时候,结点当中存储的就是<Key, Value>键值对
template<class T> struct RBTreeNode { RBTreeNode<K, V>* _left;//三叉链 RBTreeNode<K, V>* _right; RBTreeNode<K, V>* _parent; T _data;//存储的数据 Colour _col;//节点颜色 RBTreeNode(const pair<K, V>& kv) :_left(nullptr) , _right(nullptr) , _parent(nullptr) , _data(data) , _col(RED) {} };
四. 仿函数的支持
那我们插入比较的时候用data去比较吗?
对于set而言是Key,可以比较
但是对于map是pair,那我们要取其中的first来比较,但是我们能取first吗?
这个地方的data有可能是map;也有可能是set
那就只能我们自己实现一个仿函数了,如果是map那就是用于获取T当中的键值Key,当红黑树比较的时候就是仿函数去获取
仿函数,就是使一个类的使用看上去像一个函数。其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了
template<class K, class V> class map { struct MapKeyOfT { const K& operator()(const pair<K, V>& kv) { return kv.first; } } private: RBTree<K, pair<K, V>, MapKeyOfT> _t; };
底层的红黑树不知道上层传的是map还是set,因此当需要进行两个结点键值的比较时,底层红黑树都会通过传入的仿函数来获取键值Key,进而进行两个结点键值的比较
set的仿函数不可缺
template<class K> class set { struct SetKeyOfT { const K& operator()(const K& key) { return key; } } private: RBTree<K, K, SetKeyOfT> _t;
所以set容器传入底层红黑树的就是set的仿函数,map容器传入底层红黑树的就是map的仿函数
//查找函数 Node* Find(const K& key) { Node* cur = _root; Node* parent = nullptr; while (cur) { if (kot(data) > kot(cur->_data))//待插入结点的key值大于当前结点的key值 { //往节点的右子树走 parent = cur; cur = cur->_right; } else if (kot(data) < kot(cur->_data))//待插入结点的key值小于当前结点的key值 { //往节点的左子树走 parent = cur; cur = cur->_left; } else//插入的值等于当前的节点,返回失败 { return false; } }
注意:
1️⃣所有进行节点键值比较的地方,均需要通过仿函数获取对应节点的键值后再进行键值的比较
2️⃣map和set的底层是一棵泛型红黑树实例化而来,实际上不是同一棵红黑树
五. 迭代器实现
🎨正向迭代器
红黑树的正向迭代器实际上就是对结点指针进行了封装,因此在正向迭代器当中实际上就只有一个成员变量,那就是结点的指针!
//正向迭代器 template<class T, class Ref, class Ptr> struct __RBTreeIterator { typedef RBTreeNode<T> Node;//节点类型 typedef __RBTreeIterator<T, Ref, Ptr> Self;//正向迭代器类型 Node* _node;//封装节点的指针 }
通过一个节点的指针就可以封装出迭代器!
__RBTreeIterator(Node* node) :_node(node) {}
当我们对正向迭代器进行解引用操作时,我们直接返回对应结点数据的引用即可
Ref operator*() { return _node->_data;//返回节点数据的引用 }
以及成员访问操作符 ->:
Ptr operator->() { return &_node->_data;//返回节点数据的指针 }
当然,正向迭代器当中至少还需要重载==和!=运算符,实现时直接判断两个迭代器所封装的结点是否是同一个即可
bool operator!=(const Self& s) const { return _node != s._node//判断两个正向迭代器所封装的结点是否是同一个 } bool operator==(const Self& s) const { return _node == s._node//同上 }
重头戏才刚刚开始!真正的难点实际上++和--运算符的重载
一个结点的正向迭代器进行++操作后,应该根据红黑树中序遍历的序列找到当前结点的下一个结点
具体思路如下:
当前节点的右子树不为空,++就是找 右子树中序的第一个(最左节点)
如果节点的右子树为空,++就是找到 孩子在祖先左边的祖先
Self& operator++() { if (_node->_right) { //寻找该节点右子树中的最左节点 Node* left = _node->_right; while (left->_left) { left = left->_left; } _node = left;//给给变成该节点 } else { //找孩子在祖先左边的祖先 Node* parent = _node->_parent; Node* cur = _node; while (parent && cur == parent->_right) //判断parent不为空,空就崩了 { cur = cur->_parent; parent = parent->_parent; } _node = parent; } return *this; }
同理, -- 的逻辑是一样的:
当前节点的左子树不为空,--就是找 左子树中序的第一个(最右节点)
如果节点的左子树为空,--就是找到 孩子在祖先右边的祖先
Self& operator--() { if (_node->_left)//结点的左子树不为空 { //寻找该节点左子树中的最右节点 Node* right = _node->_left; while (right->_right) { right = right->_right; } _node = right;//给给变成该节点 } else//结点的左子树为空 { //找孩子在祖先右边的祖先 Node* parent = _node->_parent; Node* cur = _node; while (parent && cur == parent->_left) //判断parent不为空,空就崩了 { cur = cur->_parent; parent = parent->_parent; } _node = parent; } return *this; }
我们实现迭代器的时候会将迭代器类型进行 typedef 方便调用,完事了不要忘了迭代器还有两个成员函数 begin() 和 end() ;
begin() 返回中序序列当中第一个结点的正向迭代器,即最左节点
end ()返回中序序列当中最后一个结点下一个位置的正向迭代器,这里直接用空指针构造一个正向迭代器(不严谨的处理)
template<class K, class T, class KeyOfT> struct RBTree { typedef RBTreeNode<T> Node; public: typedef __RBTreeIterator<T, T&, T*> iterator;//正向迭代器 iterator begin() { //寻找最左节点 Node* left = _root; while (left && left->_left) { left = left->_left; } //返回最左结点的正向迭代器 return iterator(left); } iterator end() { //返回空节点的迭代器 return iterator(nullptr); } }
实际上,上述所实现的迭代器是有缺陷的,因为理论上我们对end()位置的正向迭代器进行–操作后,应该得到最后一个结点的正向迭代器,但我们实现end()时,是直接返回由nullptr构造得到的正向迭代器的,因此上述实现的代码无法完成此操作
所以我们不妨看看 C++ STL 库的实现逻辑
库里面是采用了类似双向链表的处理,给整个红黑树造了一个哨兵位节点,该节点左边指向最小的最左节点,右边指向最大的右节点,同时还有一个非常 bug 的设计就是这里哨兵位节点 header 的红黑树头结点之间的 parent 相互指向
在该结构下,实现 begin() 时,直接用头结点的左孩子构造一个正向迭代器即可,实现 rbegin() 时,直接用头结点的右孩子构造一个反向迭代器即可,严谨的过程是先构造正向迭代器,再用正向迭代器构造反向迭代器,end() 和 rend() 此时就不需要什么 nullptr 了,直接有头结点(哨兵位)进行迭代器构造即可,这样就能完成一个逻辑完整的迭代器了
🎨反向迭代器
上面得知:反向迭代器的严谨构造过程是用正向迭代器进行封装,我们可以将
template<class Iterator>//迭代器适配器 struct ReverseIterator { typedef ReverseIterator<Iterator> Self; //反向迭代器 typedef typename Iterator::reference Ref; //指针的引用 typedef typename Iterator::pointer Ptr; //结点指针 Iterator _it; //反向迭代器封装的正向迭代器 //构造函数 ReverseIterator(Iterator it) :_it(it) //根据所给正向迭代器构造一个反向迭代器 {} Ref operator*() { return *_it; //调用正向迭代器的operator*返回引用 } Ptr operator->() { return _it.operator->(); //调用正向迭代器的operator->返回指针 } Self& operator++() //前置++ { --_it; //调用正向迭代器的前置-- return *this; } //前置-- Self& operator--() { ++_it; //调用正向迭代器的前置++ return *this; } bool operator!=(const Self& s) const { return _it != s._it; } bool operator==(const Self& s) const { return _it == s._it; } };
Set的实现
都是接上红黑树的接口即可
namespace ljj { template<class K> class set { struct SetKeyOfT { const K& operator()(const K& key) { return key; } }; public: //typename告诉编译器这一大坨是类型,不是静态变量 typedef typename RBTree<K, K, SetKeyOfT>::iterator iterator; iterator begin() { return _t.begin(); } iterator end() { return _t.end(); } pair<iterator, bool> insert(const K& key) { return _t.Insert(key);//调用红黑树的insert } private: RBTree<K, K, SetKeyOfT> _t; }; }
Map的实现
map 也和 set 同理,复用红黑树的底层接口实现,此外还需要实现 [] 运算符的重载:
template<class K, class V> class map { struct MapKeyOfT { const K& operator()(const pair<K, V>& kv) { return kv.first; } }; public: //typename告诉编译器这一大坨是类型,不是静态变量 typedef typename RBTree<K, pair<K, V>, MapKeyOfT>::iterator iterator; iterator begin() { return _t.begin(); } iterator end() { return _t.end(); } pair<iterator, bool> insert(const pair<K, V>& kv) { return _t.Insert(kv);//调用红黑树的insert } //【】的底层调用就是Insert V& operator[](const K& key) { pair<iterator, bool> ret = Insert(make_pair(K, V()));//插入成功就是当前的迭代器,失败就是之前的迭代器 return ret.first->second; } private: RBTree<K, pair<K, V>, MapKeyOfT> _t; };
红黑树的代码
//枚举颜色 enum Colour { RED, BLACK }; template<class T> struct RBTreeNode { RBTreeNode<T>* _left;//三叉链 RBTreeNode<T>* _right; RBTreeNode<T>* _parent; //pair<K, V> _kv;//存储键值对 T _data; Colour _col;//节点颜色 RBTreeNode(const T& data) :_left(nullptr) , _right(nullptr) , _parent(nullptr) , _data(data) , _col(RED) {} }; //正向迭代器 template<class T, class Ref, class Ptr> struct __RBTreeIterator { typedef RBTreeNode<T> Node;//节点类型 typedef __RBTreeIterator<T, Ref, Ptr> Self;//正向迭代器类型 Node* _node;//封装节点的指针 __RBTreeIterator(Node* node) :_node(node) {} Ref operator*() { return _node->_data;//返回节点数据的引用 } Ptr operator->() { return &_node->_data;//返回节点数据的指针 } bool operator!=(const Self& s) const { return _node != s._node;//判断两个正向迭代器所封装的结点是否是同一个 } bool operator==(const Self& s) const { return _node == s._node;//同上 } Self& operator++() { if (_node->_right) { //寻找该节点右子树中的最左节点 Node* left = _node->_right; while (left->_left) { left = left->_left; } _node = left;//给给变成该节点 } else { //找孩子在祖先左边的祖先 Node* parent = _node->_parent; Node* cur = _node; while (parent && cur == parent->_right) //判断parent不为空,空就崩了 { cur = cur->_parent; parent = parent->_parent; } _node = parent; } return *this; } Self& operator--() { if (_node->_left)//结点的左子树不为空 { //寻找该节点左子树中的最右节点 Node* right = _node->_left; while (right->_right) { right = right->_right; } _node = right;//给给变成该节点 } else//结点的左子树为空 { //找孩子在祖先右边的祖先 Node* parent = _node->_parent; Node* cur = _node; while (parent && cur == parent->_left) //判断parent不为空,空就崩了 { cur = cur->_parent; parent = parent->_parent; } _node = parent; } return *this; } }; template<class K, class T, class KeyOfT> struct RBTree { typedef RBTreeNode<T> Node; public: typedef __RBTreeIterator<T, T&, T*> iterator;//正向迭代器 iterator begin() { //寻找最左节点 Node* left = _root; while (left && left->_left) { left = left->_left; } //返回最左结点的正向迭代器 return iterator(left); } iterator end() { //返回空节点的迭代器 return iterator(nullptr); } //如果是空树,则插入节点作为root节点 pair<iterator, bool> Insert(const T& data) { KeyOfT kot; if (_root == nullptr) { _root = new Node(data); _root->_col = BLACK;//根节点必须是黑色 return make_pair(iterator(_root), true); //插入成功 } //按二叉搜索树的插入方法,找到待插入位置 Node* cur = _root; Node* parent = nullptr; while (cur) { if (kot(data) > kot(cur->_data))//待插入结点的key值大于当前结点的key值 { //往节点的右子树走 parent = cur; cur = cur->_right; } else if (kot(data) < kot(cur->_data))//待插入结点的key值小于当前结点的key值 { //往节点的左子树走 parent = cur; cur = cur->_left; } else//插入的值等于当前的节点,返回失败 { return make_pair(iterator(cur), false); } } //将节点链接到树上 cur = new Node(data);//构造节点 Node* newnode = cur; cur->_col = RED; if (kot(data) < kot(parent->_data)) //判断链接左还是右? { //插入到左边 parent->_left = cur; cur->_parent = parent; } else if (kot(data) > kot(parent->_data)) { //插入到右边 parent->_right = cur; cur->_parent = parent; } //如果插入节点的父节点是红色的,则需要对红黑树进行操作 while (parent && parent->_col == RED) { Node* grandfather = parent->_parent; assert(grandfather); assert(grandfather->_col == BLACK); //关键看叔叔 ~ 判断叔叔的位置 if (parent == grandfather->_left) { Node* uncle = grandfather->_right; //情况1:uncle存在且为红 + 继续往上处理 if (uncle && uncle->_col == RED) { //变色:p和u变黑,g变红 parent->_col = uncle->_col = BLACK; grandfather->_col = RED; //继续往上调整 cur = grandfather; parent = cur->_parent; } else //情况2 + 情况3:uncle不存在 + uncle存在且为黑 { //情况二:单旋 + 变色 // g // p u //c if (cur = parent->_left) { RotateR(grandfather);//右旋 //颜色调整 parent->_col = BLACK; grandfather->_col = RED; } else//cur == parent->_right { //情况三:左右双旋 + 变色 // g // p u // c RotateL(parent); RotateR(grandfather); //调整颜色 cur->_col = BLACK; grandfather->_col = RED; } break; } } else //parent == grandfather->_right { Node* uncle = grandfather->_left; //情况1:uncle存在且为红 + 继续往上处理 if (uncle && uncle->_col == RED) { //变色:p和u变黑,g变红 parent->_col = uncle->_col = BLACK; grandfather->_col = RED; //继续往上调整 cur = grandfather; parent = cur->_parent; } else //情况2 + 情况3:uncle不存在 + uncle存在且为黑 { //情况二:单旋 + 变色 // g // u p // c if (cur = parent->_right) { RotateL(grandfather);//左单 旋 //颜色调整 parent->_col = BLACK; grandfather->_col = RED; } else//cur == parent->_left { //情况三:右左双旋 + 变色 // g // u p // c RotateR(parent); RotateL(grandfather); //调整颜色 cur->_col = BLACK; grandfather->_col = RED; } break; } } } _root->_col = BLACK;//不管什么,最后根要变黑 return make_pair(iterator(newnode), true); } void InOrder() { _InOrder(_root); cout << endl; } bool IsBalance() { if (_root == nullptr) { return true; } if (_root->_col == RED) { cout << "根节点不是黑色" << endl; return false; } // 黑色节点数量基准值 int benchmark = 0; Node* cur = _root; while (cur) { if (cur->_col == BLACK) ++benchmark; cur = cur->_left;//以最左的路径进行 } return PrevCheck(_root, 0, benchmark); } private: bool PrevCheck(Node* root, int blackNum, int& benchmark) { if (root == nullptr) { //cout << blackNum << endl; //return; if (benchmark == 0) { benchmark = blackNum; return true; } if (blackNum != benchmark) { cout << "某条黑色节点的数量不相等" << endl; return false; } else { return true; } } if (root->_col == BLACK) { ++blackNum; } //检测它的父亲 if (root->_col == RED && root->_parent->_col == RED) { cout << "存在连续的红色节点" << endl; return false; } return PrevCheck(root->_left, blackNum, benchmark) && PrevCheck(root->_right, blackNum, benchmark); } void _InOrder(Node* root) { if (root == nullptr)//空树也是红黑树 return; _InOrder(root->_left); cout << root->_kv.first << ":" << root->_kv.second << endl; _InOrder(root->_right); } //左单旋 void RotateL(Node* parent) { Node* subR = parent->_right; Node* subRL = subR->_left; Node* parentParent = parent->_parent; //建立subRL与parent之间的联系 parent->_right = subRL; if (subRL) subRL->_parent = parent; //建立parent与subR之间的联系 subR->_left = parent; parent->_parent = subR; //建立subR与parentParent之间的联系 if (parentParent == nullptr) { _root = subR; _root->_parent = nullptr; } else { if (parent == parentParent->_left) { parentParent->_left = subR; } else { parentParent->_right = subR; } subR->_parent = parentParent; } } //右单旋 void RotateR(Node* parent) { Node* subL = parent->_left; Node* subLR = subL->_right; Node* parentParent = parent->_parent; //建立subLR与parent之间的联系 parent->_left = subLR; if (subLR) subLR->_parent = parent; //建立parent与subL之间的联系 subL->_right = parent; parent->_parent = subL; //建立subL与parentParent之间的联系 if (parentParent == nullptr) { _root = subL; _root->_parent = nullptr; } else { if (parent == parentParent->_left) { parentParent->_left = subL; } else { parentParent->_right = subL; } subL->_parent = parentParent; } } //左右双旋 void RotateLR(Node* parent) { RotateL(parent->_left); RotateR(parent); } //右左双旋 void RotateRL(Node* parent) { RotateR(parent->_right); RotateL(parent); } private: Node* _root = nullptr; };