失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > AVL树和红黑树(map和set的底层实现)

AVL树和红黑树(map和set的底层实现)

时间:2021-12-16 20:12:01

相关推荐

AVL树和红黑树(map和set的底层实现)

map和set的概念及使用

map和set的底层结构

map和set其底层都是按照二叉搜索树来实现的,但是二叉搜索树有其自身的缺陷,假如往树中插入的元素有序或者接近有序,二叉搜索树就会退化成单支树,时间复杂度会退化成O(N),因此map、set等关联式容器的底层结构是对二叉树进行了平衡处理,即采用平衡树来实现。

AVL树

AVL树的概念

二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:

它的左右子树都是AVL树左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)

如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在 O(logN),搜索时间复杂度O(logN )。

AVL树节点的定义

template<class K, class V>struct AVLTreeNode{pair<K, V> _val;AVLTreeNode<K, V>* _pLeft;AVLTreeNode<K, V>* _pRight;AVLTreeNode<K, V>* _pParent;int _bf;AVLTreeNode(const pair<K,V>& val):_val(val), _pLeft(nullptr)//该节点的左孩子, _pRight(nullptr)//该节点的右孩子, _pParent(nullptr)//该节点的父节点, _bf(0)//该节点的平衡因子{}};

AVL树的插入

AVL树就是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看成是二叉搜索树。那么AVL树的插入过程可以分为两步:

按照二叉搜索树的方式插入新节点调整节点的平衡因子

bool Insert(const pair<K, V> kv){// 1. 先按照二叉搜索树的规则将节点插入到AVL树中// ...// 2. 新节点插入后,AVL树的平衡性可能会遭到破坏,此时就需要更新平衡因子,并检测是否破坏了AVL树的平衡性/*pCur插入后,pParent的平衡因子一定需要调整,在插入之前,pParent的平衡因子分为三种情况:-1,0, 1, 分以下两种情况:1. 如果pCur插入到pParent的左侧,只需给pParent的平衡因子-1即可2. 如果pCur插入到pParent的右侧,只需给pParent的平衡因子+1即可此时:pParent的平衡因子可能有三种情况:0,正负1, 正负21. 如果pParent的平衡因子为0,说明插入之前pParent的平衡因子为正负1,插入后被调整成0,此时满足AVL树的性质,插入成功2. 如果pParent的平衡因子为正负1,说明插入前pParent的平衡因子一定为0,插入后被更新成正负1,此时以pParent为根的树的高度增加,需要继续向上更新3. 如果pParent的平衡因子为正负2,则pParent的平衡因子违反平衡树的性质,需要对其进行旋转处理*/if (_root == nullptr){_root = new Node(kv);_root->_bf = 0;return true;}Node* Parent = nullptr;Node* cur = _root;while (cur){if (cur->_val.first > kv.first){Parent = cur;cur = cur->_pLeft;}else if (cur->_val.first < kv.first){Parent = cur;cur = cur->_pRight;}else{return false;}}cur = new Node(kv);if (Parent->_val > cur->_val){Parent->_pLeft = cur;cur->_pParent = Parent;}else{Parent->_pRight = cur;cur->_pParent = Parent;}while (Parent){if (cur == Parent->_pLeft)Parent->_bf--;else{Parent->_bf++;}if (Parent->_bf == 0)break;else if (abs(Parent->_bf) == 1){cur = Parent;Parent = Parent->_pParent;}else if (abs(Parent->_bf) == 2){if (Parent->_bf == 2){if (cur->_bf == 1){RotateL(Parent);}else if (cur->_bf == -1){RotateRL(Parent);}}else if (Parent->_bf == -2){if (cur->_bf == -1){RotateR(Parent);}else if (cur->_bf == 1){RotateLR(Parent);}}break;}else{assert(false);}}return true;}

AVL树的旋转

如果在一棵原本是平衡的AVL树中插入一个新节点,可能造成不平衡,此时必须调整树的结构,使之平衡化。根据节点插入位置的不同,AVL树的旋转分为四种:

1. 新节点插入较高左子树的左侧—左左:右单旋

/*上图在插入前,AVL树是平衡的,新节点插入到30的左子树(注意:此处不是左孩子)中,30左子树增加了一层,导致以60为根的二叉树不平衡,要让60平衡,只能将60左子树的高度减少一层,右子树增加一层,即将左子树往上提,这样60转下来,因为60比30大,只能将其放在30的右子树,而如果30有右子树,右子树根的值一定大于30,小于60,只能将其放在60的左子树,旋转完成后,更新节点的平衡因子即可。在旋转过程中,有以下几种情况需要考虑:1. 30节点的右孩子可能存在,也可能不存在2. 60可能是根节点,也可能是子树如果是根节点,旋转完成后,要更新根节点如果是子树,可能是某个节点的左子树,也可能是右子树同学们再此处可举一些详细的例子进行画图,考虑各种情况,加深旋转的理解*/void _RotateR(PNode pParent){// pSubL: pParent的左孩子// pSubLR: pParent左孩子的右孩子,注意:该PNode pSubL = pParent->_pLeft;PNode pSubLR = pSubL->_pRight;// 旋转完成之后,30的右孩子作为双亲的左孩子pParent->_pLeft = pSubLR;// 如果30的左孩子的右孩子存在,更新亲双亲if(pSubLR)pSubLR->_pParent = pParent;// 60 作为 30的右孩子pSubL->_pRight = pParent;// 因为60可能是棵子树,因此在更新其双亲前必须先保存60的双亲PNode pPParent = pParent->_pParent;// 更新60的双亲pParent->_pParent = pSubL;// 更新30的双亲pSubL->_pParent = pPParent;// 如果60是根节点,根新指向根节点的指针if(NULL == pPParent){_pRoot = pSubL;pSubL->_pParent = NULL;}else{// 如果60是子树,可能是其双亲的左子树,也可能是右子树if(pPParent->_pLeft == pParent)pPParent->_pLeft = pSubL;elsepPParent->_pRight = pSubL;}// 根据调整后的结构更新部分节点的平衡因子pParent->_bf = pSubL->_bf = 0;}

2. 新节点插入较高右子树的右侧—右右:左单旋

//左单旋void RotateL(Node* parent){Node* subR = parent->_pRight;Node* subRL = subR->_pLeft;parent->_pRight = subRL;if (subRL)subRL->_pParent = parent;subR->_pLeft = parent;Node* ppNode = parent->_pParent;parent->_pParent = subR;if (parent == _root){_root = subR;_root->_pParent = nullptr;}else{if (ppNode->_pLeft == subR)ppNode->_pLeft = subR;else{ppNode->_pRight = subR;}subR->_pParent = ppNode;}parent->_bf = subR->_bf = 0;}

3. 新节点插入较高左子树的右侧—左右:先左单旋再右单旋

将双旋变成单旋后再旋转,即:先对30进行左单旋,然后再对90进行右单旋,旋转完成后再考虑平衡因子的更新。

// 旋转之前,60的平衡因子可能是-1/0/1,旋转完成之后,根据情况对其他节点的平衡因子进行调整void _RotateLR(PNode pParent){PNode pSubL = pParent->_pLeft;PNode pSubLR = pSubL->_pRight;// 旋转之前,保存pSubLR的平衡因子,旋转完成之后,需要根据该平衡因子来调整其他节点的平衡因子int bf = pSubLR->_bf;// 先对30进行左单旋_RotateL(pParent->_pLeft);// 再对90进行右单旋_RotateR(pParent);if(1 == bf)pSubL->_bf = -1;else if(-1 == bf)pParent->_bf = 1;}

4. 新节点插入较高右子树的左侧—右左:先右单旋再左单旋

void RotateRL(Node* parent){Node* subR = parent->_pRight;Node* subRL = subR->_pLeft;int bf = subRL->_bf;//保存subRL的平衡因子,因为在下面两行会置 0RotateR(parent->_pRight);RotateL(parent);if (bf == 0){parent->_bf = subRL->_bf = subR->_bf = 0;}else if (bf == 1){//在c插入的新结点subR->_bf = 0;parent->_bf = -1;subRL->_bf = 0;}else if (bf == -1){//在b插入的新结点parent->_bf = 0;subR->_bf = 1;subRL->_bf = 0;}}

总结:

假如以pParent为根的子树不平衡,即pParent的平衡因子为2或者-2,分以下情况考虑

pParent的平衡因子为2,说明pParent的右子树高,设pParent的右子树的根为pSubR 当pSubR的平衡因子为1时,执行左单旋当pSubR的平衡因子为-1时,执行右左双旋 pParent的平衡因子为-2,说明pParent的左子树高,设pParent的左子树的根为pSubL 当pSubL的平衡因子为-1是,执行右单旋当pSubL的平衡因子为1时,执行左右双旋

旋转完成后,原pParent为根的子树个高度降低,已经平衡,不需要再向上更新。

AVL树的验证

AVL树是在二叉搜索树的基础上加入了平衡性的限制,因此要验证AVL树,可以分两步:

验证其为二叉搜索树

如果中序遍历可得到一个有序的序列,就说明为二叉搜索树验证其为平衡树

每个节点子树高度差的绝对值不超过1(注意节点中如果没有平衡因子)节点的平衡因子是否计算正确

int _Height(PNode pRoot);bool _IsBalanceTree(PNode pRoot){// 空树也是AVL树if (nullptr == pRoot) return true;// 计算pRoot节点的平衡因子:即pRoot左右子树的高度差int leftHeight = _Height(pRoot->_pLeft);int rightHeight = _Height(pRoot->_pRight);int diff = rightHeight - leftHeight;// 如果计算出的平衡因子与pRoot的平衡因子不相等,或者// pRoot平衡因子的绝对值超过1,则一定不是AVL树if (diff != pRoot->_bf || (diff > 1 || diff < -1))return false;// pRoot的左和右如果都是AVL树,则该树一定是AVL树return _IsBalanceTree(pRoot->_pLeft) && _IsBalanceTree(pRoot->_pRight);}

AVL树的删除

因为AVL树也是二叉搜索树,可按照二叉搜索树的方式将节点删除,然后再更新平衡因子,只不错与删除不同的时,删除节点后的平衡因子更新,最差情况下一直要调整到根节点的位置。

AVL树的性能

AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这样可以保证查询时高效的时间复杂度,即 。但是如果要对AVL树做一些结构修改的操作,性能非常低下,比如:

插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。

因此:如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,但一个结构经常修改,就不太适合。

红黑树

红黑树的概念

红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。

红黑树的性质

每个结点不是红色就是黑色根节点是黑色的如果一个节点是红色的,则它的两个孩子结点是黑色的对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点每个叶子结点都是黑色的(此处的叶子结点指的是空结点)

红黑树节点的定义

// 节点的颜色enum Color{RED, BLACK};// 红黑树节点的定义template<class ValueType>struct RBTreeNode{RBTreeNode(const ValueType& data = ValueType(),Color color = RED): _pLeft(nullptr), _pRight(nullptr), _pParent(nullptr), _data(data), _color(color){}RBTreeNode<ValueType>* _pLeft; // 节点的左孩子RBTreeNode<ValueType>* _pRight; // 节点的右孩子RBTreeNode<ValueType>* _pParent; // 节点的双亲(红黑树需要旋转,为了实现简单给出该字段)ValueType _data; // 节点的值域Color _color;// 节点的颜色};

红黑树结构

为了后续实现关联式容器简单,红黑树的实现中增加一个头结点,因为跟节点必须为黑色,为了与根节点进行区分,将头结点给成黑色,并且让头结点的 pParent 域指向红黑树的根节点,pLeft域指向红黑树中最小的节点,_pRight域指向红黑树中最大的节点,如下:

红黑树的插入操作

红黑树是在二叉搜索树的基础上加上其平衡限制条件,因此红黑树的插入可分为两步:

1. 按照二叉搜索的树规则插入新节点

template<class ValueType>class RBTree{typedef RBTreeNode Node;public:bool Insert(const ValueType& data){……PNode& pRoot = GetRoot();if (nullptr == pRoot){pRoot = new Node(data, BLACK);// 根的双亲为头节点pRoot->_pParent = _pHead;_pHead->_pParent = pRoot;}else{// 1. 按照二叉搜索的树方式插入新节点// 2. 检测新节点插入后,红黑树的性质是否造到破坏,// 若满足直接退出,否则对红黑树进行旋转着色处理}// 根节点的颜色可能被修改,将其改回黑色pRoot->_color = BLACK;_pHead->_pLeft = LeftMost();_pHead->_pRight = RightMost();return true;}private:PNode& GetRoot(){return _pHead->_pParent; }// 获取红黑树中最小节点,即最左侧节点PNode LeftMost();// 获取红黑树中最大节点,即最右侧节点PNode RightMost();private:Node* _root;};

2. 检测新节点插入后,红黑树的性质是否造到破坏

因为新节点的默认颜色是红色,因此:如果其双亲节点的颜色是黑色,没有违反红黑树任何性质,则不需要调整;但当新插入节点的双亲节点颜色为红色时,就违反了性质三不能有连在一起的红色节点,此时需要对红黑树分情况来讨论:

约定:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点情况一: cur为红,p为红,g为黑,u存在且为红

解决方式:将p,u改为黑,g改为红,然后把g当成cur,继续向上调整。情况二: cur为红,p为红,g为黑,u不存在/u为黑

p为g的左孩子,cur为p的左孩子,则进行右单旋转;相反,

p为g的右孩子,cur为p的右孩子,则进行左单旋转

p、g变色–p变黑,g变红情况三:cur为红,p为红,g为黑,u不存在/u为黑

p为g的左孩子,cur为p的右孩子,则针对p做左单旋转;相反,

p为g的右孩子,cur为p的左孩子,则针对p做右单旋转

则转换成了情况2

pair<iterator, bool> Insert(const T& val){//插入节点if (_root == nullptr){_root = new Node(val);_root->_color = BLACK;return make_pair(iterator(_root), true);}//寻找位置Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_data < val){parent = cur;cur = cur->_pRight;}else if (cur->_data > val){parent = cur;cur = cur->_pLeft;}else{return make_pair(iterator(cur), false);}}cur = new Node(val);Node* newnode = cur;cur->_color = RED;//插入适当位置if (parent->_data < val){parent->_pRight = cur;cur->_pParent = parent;}else{parent->_pLeft = cur;cur->_pParent = parent;}//变色处理//父节点为红色节点while (parent && parent->_color == RED){Node* grandfather = parent->_pParent;//父亲是祖父节点左孩子if (parent == grandfather->_pLeft){Node* uncle = grandfather->_pRight;// 1.叔叔节点存在且为红if (uncle && uncle->_color == RED){parent->_color = BLACK;uncle->_color = BLACK;grandfather->_color = RED;cur = grandfather;parent = cur->_pParent;}//叔叔节点不存在或者叔叔节点为黑else{if (cur == parent->_pRight){RotateL(parent);swap(parent, cur);}RotateR(grandfather);parent->_color = BLACK;grandfather->_color = RED;break;}}//父亲节点是祖父节点右孩子else{Node* uncle = grandfather->_pLeft;if (uncle && uncle->_color == RED){parent->_color = BLACK;uncle->_color = BLACK;grandfather->_color = RED;cur = grandfather;parent = cur->_pParent;}else{if (cur == parent->_pLeft){RotateR(parent);swap(parent, cur);}RotateL(grandfather);parent->_color = BLACK;grandfather->_color = RED;break;}}}_root->_color = BLACK;return make_pair(iterator(newnode), true);}void RotateL(Node* parent){Node* subR = parent->_pRight;Node* subRL = subR->_pLeft;parent->_pRight = subRL;if (subRL)subRL->_pParent = parent;subR->_pLeft = parent;Node* pNode = parent->_pParent;parent->_pParent = subR;if (parent == _root){_root = subR;_root->_pParent = nullptr;}else{if (pNode->_pLeft == parent)pNode->_pLeft = subR;elsepNode->_pRight = subR;subR->_pParent = pNode;}}void RotateR(Node* parent){Node* subL = parent->_pLeft;Node* subLR = subL->_pRight;parent->_pLeft = subLR;if (subLR)subLR->_pParent = parent;subL->_pRight = parent;Node* pNode = parent->_pParent;parent->_pParent = subL;if (pNode == nullptr){_root = subL;_root->_pParent = nullptr;}else{if (pNode->_pLeft == parent){pNode->_pLeft = subL;}else{pNode->_pRight = subL;}subL->_pParent = pNode;}}

红黑树的验证

红黑树的检测分为两步:

检测其是否满足二叉搜索树(中序遍历是否为有序序列)检测其是否满足红黑树的性质

bool IsValidRBTree(){Node* pRoot = _root;// 空树也是红黑树if (nullptr == pRoot)return true;// 检测根节点是否满足情况if (BLACK != pRoot->_color){cout << "违反红黑树性质二:根节点必须为黑色" << endl;return false;}// 获取任意一条路径中黑色节点的个数size_t blackCount = 0;Node* pCur = pRoot;while (pCur){if (BLACK == pCur->_color)blackCount++;pCur = pCur->_pLeft;}// 检测是否满足红黑树的性质,k用来记录路径中黑色节点的个数size_t k = 0;return _IsValidRBTree(pRoot, k, blackCount);}bool _IsValidRBTree(Node* pRoot, size_t k, const size_t blackCount){//走到null之后,判断k和black是否相等if (nullptr == pRoot){if (k != blackCount){cout << "违反性质四:每条路径中黑色节点的个数必须相同" << endl;return false;}return true;}// 统计黑色节点的个数if (BLACK == pRoot->_color)k++;// 检测当前节点与其双亲是否都为红色Node* pParent = pRoot->_pParent;if (pParent && RED == pParent->_color && RED == pRoot->_color){cout << "违反性质三:没有连在一起的红色节点" << endl;return false;}return _IsValidRBTree(pRoot->_pLeft, k, blackCount) &&_IsValidRBTree(pRoot->_pRight, k, blackCount);}

红黑树与AVL树的比较

红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O(log N),红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。

红黑树的迭代器

迭代器的好处是可以方便遍历,是数据结构的底层实现与用户透明。如果想要给红黑树增加迭代器,需要考虑以前问题:

begin()与end()

STL明确规定,begin()与end()代表的是一段前闭后开的区间,而对红黑树进行中序遍历后,可以得到一个有序的序列,因此:begin()可以放在红黑树中最小节点(即最左侧节点)的位置,end()放在最大节点

(最右侧节点)的下一个位置,关键是最大节点的下一个位置在哪块?能否给成nullptr呢?答案是行不通的,因为对end()位置的迭代器进行–操作,必须要能找最后一个元素,此处就不行,因此最好的方式是将end()放在头结点的位置:

// 找迭代器的下一个节点,下一个节点肯定比其大void Increasement(){//分两种情况讨论:_pNode的右子树存在和不存在// 右子树存在if(_pNode->_pRight){// 右子树中最小的节点,即右子树中最左侧节点_pNode = _pNode->_pRight;while(_pNode->_pLeft)_pNode = _pNode->_pLeft;}else{// 右子树不存在,向上查找,直到_pNode != pParent->rightPNode pParent = _pNode->_pParent;while(pParent->_pRight == _pNode){_pNode = pParent;pParent = _pNode->_pParent;}// 特殊情况:根节点没有右子树if(_pNode->_pRight != pParent)_pNode = pParent;}}// 获取迭代器指向节点的前一个节点void Decreasement(){//分三种情况讨论:_pNode 在head的位置,_pNode 左子树存在,_pNode 左子树不存在// 1. _pNode 在head的位置,--应该将_pNode放在红黑树中最大节点的位置if(_pNode->_pParent->_pParent == _pNode && _pNode->_color == RED)_pNode = _pNode->_pRight;else if(_pNode->_pLeft){// 2. _pNode的左子树存在,在左子树中找最大的节点,即左子树中最右侧节点_pNode = _pNode->_pLeft;while(_pNode->_pRight)_pNode = _pNode->_pRight;}else{// _pNode的左子树不存在,只能向上找PNode pParent = _pNode->_pParent;while(_pNode == pParent->_pLeft){_pNode = pParent;pParent = _pNode->_pParent;}_pNode = pParent;}}

RBTree的完整代码

#include<iostream>using namespace std;enum Color{RED,BLACK};template<class ValueType>struct RBTreeNode{RBTreeNode<ValueType>* _pLeft;RBTreeNode<ValueType>* _pRight;RBTreeNode<ValueType>* _pParent;ValueType _data;Color _color;RBTreeNode(const ValueType& data = ValueType(),Color color = RED):_pLeft(nullptr), _pRight(nullptr), _pParent(nullptr), _data(data), _color(color){}};template<class T,class Ptr,class Ref>struct RBTreeIterator{typedef RBTreeNode<T> Node;typedef RBTreeIterator<T, Ptr, Ref> Self;Node* _node;RBTreeIterator(Node* node):_node(node){}Ref operator*(){return _node->_data;}Ref operator->(){return &_node->_data;}Self& operator++(){if (_node->_pRight != nullptr){_node = _node->_pRight;while (_node->_pLeft != nullptr){_node = _node->_pLeft;}}else{Node* parent = _node->_pParent;while (parent != nullptr && _node == parent->_pRight){_node = parent;parent = _node->_parent;}_node = parent;}return *this;}Self operator++(int){Self tmp(*this);++(*this);return tmp;}Self& operator--(){if (_node->_pLeft != nullptr){_node = _node->_pLeft;while (_node->_pLeft != nullptr){_node = _node->_pRight;}}else{Node* parent = _node->_pParent;while (parent != nullptr && _node == parent->_pLeft){_node = parent;parent = _node->_parent;}_node = parent;}return *this;}Self operator--(int){Self tmp(*this);--(*this);return tmp;}bool operator != (const Self& s) const{return _node != s._node;}bool operator == (const Self& s) const{return _node == s._node;}};template<class K, class T>class RBTree{typedef RBTreeNode<T> Node;public:typedef RBTreeIterator<T, T*, T&> iterator;typedef RBTreeIterator<T, const T*, const T&> const_iterator;RBTree() = default;// 拷贝构造 + operator=// 析构函数iterator begin(){Node* cur = _root;while (cur && cur->_left){cur = cur->_left;}return iterator(cur);}iterator end(){return iterator(nullptr);}const_iterator begin() const{Node* cur = _root;while (cur && cur->_left){cur = cur->_left;}return const_iterator(cur);}const_iterator end() const{return const_iterator(nullptr);}pair<iterator, bool> Insert(const T& val){//插入节点if (_root == nullptr){_root = new Node(val);_root->_color = BLACK;return make_pair(iterator(_root), true);}//寻找位置Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_data < val){parent = cur;cur = cur->_pRight;}else if (cur->_data > val){parent = cur;cur = cur->_pLeft;}else{return make_pair(iterator(cur), false);}}cur = new Node(val);Node* newnode = cur;cur->_color = RED;//插入适当位置if (parent->_data < val){parent->_pRight = cur;cur->_pParent = parent;}else{parent->_pLeft = cur;cur->_pParent = parent;}//变色处理//父节点为红色节点while (parent && parent->_color == RED){Node* grandfather = parent->_pParent;//父亲是祖父节点左孩子if (parent == grandfather->_pLeft){Node* uncle = grandfather->_pRight;// 1.叔叔节点存在且为红if (uncle && uncle->_color == RED){parent->_color = BLACK;uncle->_color = BLACK;grandfather->_color = RED;cur = grandfather;parent = cur->_pParent;}//叔叔节点不存在或者叔叔节点为黑else{if (cur == parent->_pRight){RotateL(parent);swap(parent, cur);}RotateR(grandfather);parent->_color = BLACK;grandfather->_color = RED;break;}}//父亲节点是祖父节点右孩子else{Node* uncle = grandfather->_pLeft;if (uncle && uncle->_color == RED){parent->_color = BLACK;uncle->_color = BLACK;grandfather->_color = RED;cur = grandfather;parent = cur->_pParent;}else{if (cur == parent->_pLeft){RotateR(parent);swap(parent, cur);}RotateL(grandfather);parent->_color = BLACK;grandfather->_color = RED;break;}}}_root->_color = BLACK;return make_pair(iterator(newnode), true);}void RotateL(Node* parent){Node* subR = parent->_pRight;Node* subRL = subR->_pLeft;parent->_pRight = subRL;if (subRL)subRL->_pParent = parent;subR->_pLeft = parent;Node* pNode = parent->_pParent;parent->_pParent = subR;if (parent == _root){_root = subR;_root->_pParent = nullptr;}else{if (pNode->_pLeft == parent)pNode->_pLeft = subR;elsepNode->_pRight = subR;subR->_pParent = pNode;}}void RotateR(Node* parent){Node* subL = parent->_pLeft;Node* subLR = subL->_pRight;parent->_pLeft = subLR;if (subLR)subLR->_pParent = parent;subL->_pRight = parent;Node* pNode = parent->_pParent;parent->_pParent = subL;if (pNode == nullptr){_root = subL;_root->_pParent = nullptr;}else{if (pNode->_pLeft == parent){pNode->_pLeft = subL;}else{pNode->_pRight = subL;}subL->_pParent = pNode;}}iterator Find(const K& k){Node* cur = _root;while (cur){KeyOfValue kov;if (kov(cur->_data) < k){cur = cur->_pRight;}else if (kov(cur->_data) > k){cur = cur->_pLeft;}else{return iterator(cur);}}return end();}bool IsValidRBTree(){Node* pRoot = _root;// 空树也是红黑树if (nullptr == pRoot)return true;// 检测根节点是否满足情况if (BLACK != pRoot->_color){cout << "违反红黑树性质二:根节点必须为黑色" << endl;return false;}// 获取任意一条路径中黑色节点的个数size_t blackCount = 0;Node* pCur = pRoot;while (pCur){if (BLACK == pCur->_color)blackCount++;pCur = pCur->_pLeft;}// 检测是否满足红黑树的性质,k用来记录路径中黑色节点的个数size_t k = 0;return _IsValidRBTree(pRoot, k, blackCount);}bool _IsValidRBTree(Node* pRoot, size_t k, const size_t blackCount){//走到null之后,判断k和black是否相等if (nullptr == pRoot){if (k != blackCount){cout << "违反性质四:每条路径中黑色节点的个数必须相同" << endl;return false;}return true;}// 统计黑色节点的个数if (BLACK == pRoot->_color)k++;// 检测当前节点与其双亲是否都为红色Node* pParent = pRoot->_pParent;if (pParent && RED == pParent->_color && RED == pRoot->_color){cout << "违反性质三:没有连在一起的红色节点" << endl;return false;}return _IsValidRBTree(pRoot->_pLeft, k, blackCount) &&_IsValidRBTree(pRoot->_pRight, k, blackCount);}void InOrder(){_InOrder(_root);cout << endl;}void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_pLeft);cout << root->_data << " ";_InOrder(root->_pRight);}private:Node* _root = nullptr;};void TestRBtree(){RBTree<int, int> t;int a[] = {16, 3, 7, 11, 9, 26, 18, 14, 15 };//int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14, };for (auto e : a){t.Insert(e);}t.InOrder();cout << t.IsValidRBTree() << endl;}

map的模拟实现

#include"RBTree.h"namespace fxl{template<class K, class V>class map{typedef pair<K, V> ValueType;// 作用:将value中的key提取出来struct KeyOfValue{const K& operator()(const ValueType& v){return v.first;}};typedef RBTree<K, ValueType, KeyOfValue> RBTree;public:typedef typename RBTree::Iterator iterator;public:map(){}iterator begin(){return _t.Begin(); }iterator end(){return _t.End(); }size_t size()const{return _t.Size(); }bool empty()const{return _t.Empty(); }V& operator[](const K& key){return (*(_t.Insert(ValueType(key, V()))).first).second;}const V& operator[](const K& key)const;pair<iterator, bool> insert(const ValueType& data) {return _t.Insert(data); }void clear(){_t.Clear(); }iterator find(const K& key){return _t.Find(key); }private:RBTree _t;};}

set的模拟实现

#include"RBTree.h"namespace fxl{template<class K>class set{typedef K ValueType;struct KeyOfValue{const K& operator()(const ValueType& key){return key;}};// 红黑树类型重命名typedef RBTree<K, ValueType, KeyOfValue> RBTree;public:typedef typename RBTree::Iterator iterator;public:Set(){}iterator begin(){return _t.Begin(); }iterator end(){return _t.End(); }size_t size()const{return _t.Size(); }bool empty()const{return _t.Empty(); }pair<iterator, bool> insert(const ValueType& data){return _t.Insert(data);}void clear(){_t.Clear(); }iterator find(const K& key){return _t.Find(key); }private:RBTree _t;};}

如果觉得《AVL树和红黑树(map和set的底层实现)》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。