【c++底层结构】AVL树红黑树

【c++底层结构】AVL树&红黑树

  • 1.AVL树
    • 1.1 AVL树的概念
    • 1.2 AVL树结点的定义
    • 1.3 AVL树的插入
    • 1.4 AVL树的旋转
    • 1.5 AVL树的验证
    • 1.6 AVL树的性能
  • 2. 红黑树
    • 2.1 红黑树的概念
    • 2.2 红黑树的性质
    • 2.3 红黑树节点的定义
    • 2.4 红黑树的插入操作
    • 2.5 红黑树的验证
    • 2.6 红黑树与AVL树的比较
    • 2.7 红黑树的应用
    • 2.8 红黑树模拟实现STL中的map与set
  • 3 .map和set的模拟实现
    • 3.1 map的模拟实现
    • 3.2 set的模拟实现
    • 3.3 改造红黑树

1.AVL树

1.1 AVL树的概念

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

  • 它的左右子树都是AVL树
  • 左右子树高度之差(平衡因子)的绝对值不超过1(-1/1/0)
    在这里插入图片描述 如果一颗二叉树是高度平衡的,它就是AVL、如果它有n个结点,其高度可保持在O(log2N),搜索时间复杂度O(log2N)

1.2 AVL树结点的定义

template<class K,class V>
class AVLTreeNode
{AVLTreeNode<K, V>* _left;   //该节点的左孩子   AVLTreeNode<K, V>* _right;  //该节点的右孩子AVLTreeNode<K, V>* _parent; //该节点的父结点pair<K, V> _kv;             //该节点的左孩子int _bf;//balance factor    //该节点的平衡因子AVLTreeNode(const pair<K, V>& kv):_left(nullptr)     , _right(nullptr)   , _parent(nullptr)  , _kv(kv)          , _bf(0)            {}
};

1.3 AVL树的插入

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

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

1、更新平衡因子

1、cur == parent->_right parent->bf++
2、cur == parent->_left parent->bf–

2、如果更新完以后,平衡因子没有出现问题,(|bf|<=1)平衡结构没有受到影响,不需要处理
3、如果更新完以后,平衡出现问题,平衡结构受到影响,需要处理(旋转)

什么决定了是否要继续往上更新,parent所在的子树高度是否变化?变了继续更新,不变就不再更新。

1、parent->bf == 1 || parent->bf == -1 --> parent所在的子树变了,继续更新。(0->1 ||0->-1)
2、parent->bf==2||parent->bf == -2 -->parent所在的子树不平衡。需要处理这棵子树(旋转处理)。
3、parent->bf == 0 parent所在的子树高度不变,不用继续往上更新,插入结束。(-1->0,1->0,parent子树高度不变)

1.4 AVL树的旋转

旋转的原则:保持搜索树的性质
旋转的目的:左右均衡,降低整棵树的高度

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

首先我们来分析两种简单的旋转(单旋)
我们可以将需要单旋的情况抽象为下图所示的抽象图
在这里插入图片描述
简单分析当h=1;h=2;h=3
在这里插入图片描述

  1. 新节点插入较高右子树的右侧—右右:左单旋
    在这里插入图片描述
void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;//链接parent和subRL ,并更新三叉链parent->_right = subRL;if(subRL!=nullptr)subRL->_parent = parent;Node* ppnode = parent->_parent;//链接subR和parent,并更新三叉链subR->_left = parent;parent->_parent = subR;//parent为根节点if (ppnode == nullptr){_root = subR;_root->_parent = nullptr;}//parent不为根节点else{//与ppnode链接if (ppnode->_left == parent){ppnode->_left = subR}else{ppnode->_right = subR;;}subR->_parent = ppnode;}//更新平衡因子parent->_bf = subR->_bf = 0;}
  1. 新节点插入较高左子树的左侧–左左:右单旋
    在这里插入图片描述

代码

void RotateR(Node * parent){Node* subL = parent->_left;Node* subLR = subL->_right;Node* ppnode = parent->_parent;subL->_right = parent;parent->_parent = subL;parent->_left = subLR;if (subLR != nullptr)subLR->_parent = parent;if (ppnode == nullptr){_root = subL;_root->_parent = nullptr;}else{if (ppnode__left == parent){ppnode->_left = subL;}else{ppnode->_right = subL;}subL->_parent = ppnode;}parent->_bf = subL->_bf = 0;}

接下来我们继续分析双旋的情况。
抽象图如下
在这里插入图片描述

具象图如图下

在这里插入图片描述

  1. 新节点插入较高左子树的右侧—左右:先左单旋再右单旋
    在这里插入图片描述
	void RotateLR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;int bf = subLR->_bf;RotateL(parent->_left);RotateR(parent);if (bf == 1){parent->_bf = 0;subLR->_bf = 0;subL->_bf = -1;}else if (bf == -1){parent->_bf = 1;subLR->_bf = 0;subL->_bf = 0;}else if (bf == 0){parent->_bf = 0;subLR->_bf = 0;subL->_bf = 0;}else{assert(false);}}
  1. 新节点插入较高右子树的左侧—右左:先右单旋再左单旋
    在这里插入图片描述
	RotateRL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;int bf = subRL->_bf;RotateR(parent->_right);RotateL(parent);if (bf == 1){parent->_bf = -1;subRL->_bf = 0;subR->_bf = 0;}else if (bf == -1){parent->_bf = 0;subRL->_bf = 0;subR->_bf = 1;}else if (bf == 0){parent->_bf = 0;subRL->_bf = 0;subR->_bf = 0;}else{assert(false);}}

1.5 AVL树的验证

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

  1. 验证其为二叉搜索树
    如果中序遍历可得到一个有序的序列,就说明为二叉搜索树
  2. 验证其为平衡树
    每个节点子树高度差的绝对值不超过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);
}

1.6 AVL树的性能

AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这样可以保证
查询时高效的时间复杂度,即 。但是如果要对AVL树做一些结构修改的操作,性能非常低下,比如:
插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。
因此:如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,
但一个结构经常修改,就不太适合。
全部代码

#pragma once
#include<assert.h>
#include<iostream>
#include<set>
#include<map>
#include<string>
using namespace std;
template<class K,class V>
struct AVLTreeNode
{AVLTreeNode<K, V>* _left;      AVLTreeNode<K, V>* _right;AVLTreeNode<K, V>* _parent;pair<K, V> _kv;int _bf;//balance factorAVLTreeNode(const pair<K, V>& kv):_left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv), _bf(0){}
};
template <class K,class V>
class AVLTree
{typedef AVLTreeNode<K, V> Node;
public:bool Insert(const pair<K, V>& kv){if (_root == nullptr){_root = new Node(kv);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);if (parent->_kv.first > kv.first){parent->_left = cur;}else{parent->_right = cur;}cur->_parent = parent;//更新平衡因子while (parent){if (cur == parent->_right){parent->_bf++;}else{parent->_bf--;}if (parent->_bf == 1 || parent->_bf == -1){//继续更新parent = parent->_parent;cur = cur->_parent;}else if (parent->_bf == 0){break;}else if(parent->_bf == 2 || parent->_bf == -2){//旋转处理  1、让子树平衡,2、降低这棵子树的高度if (parent->_bf == 2 && cur->_bf == 1){RotateL(parent);}else if (parent->_bf == -2 && cur->_bf == -1){RotateR(parent);}else if (parent->_bf == -2 && cur->_bf == 1){RotateLR(parent);}else if (parent->_bf == 2 && cur->_bf == -1){RotateRL(parent);}else{assert(false);}break;}//在插入前平衡结构已经出现问题else{assert(false);}}return true;}void InOrder(){_InOrder(_root);cout << endl;}bool IsBalance(){return _IsBalance(_root);}int Height(){return _Height(_root);}
private:void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);cout << root->_kv.first << " ";_InOrder(root->_right);}int _Height(Node* root){if (root == NULL)return 0;int leftH = _Height(root->_left);int rightH = _Height(root->_right);return leftH > rightH ? leftH + 1 : rightH + 1;}bool _IsBalance(Node* root){if (root == NULL){return true;}int leftH = _Height(root->_left);int rightH = _Height(root->_right);if (rightH - leftH != root->_bf){cout << root->_kv.first << "节点平衡因子异常" << endl;return false;}return abs(leftH - rightH) < 2&& _IsBalance(root->_left)&& _IsBalance(root->_right);}void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;//链接parent和subRL ,并更新三叉链parent->_right = subRL;if(subRL!=nullptr)subRL->_parent = parent;Node* ppnode = parent->_parent;//链接subR和parent,并更新三叉链subR->_left = parent;parent->_parent = subR;//parent为根节点if (ppnode == nullptr){_root = subR;_root->_parent = nullptr;}//parent不为根节点else{//与ppnode链接if (ppnode->_left == parent){ppnode->_left = subR;}else{ppnode->_right = subR;;}subR->_parent = ppnode;}//更新平衡因子parent->_bf = subR->_bf = 0;}void RotateR(Node * parent){Node* subL = parent->_left;Node* subLR = subL->_right;Node* ppnode = parent->_parent;subL->_right = parent;parent->_parent = subL;parent->_left = subLR;if (subLR != nullptr)subLR->_parent = parent;if (ppnode == nullptr){_root = subL;_root->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = subL;}else{ppnode->_right = subL;}subL->_parent = ppnode;}parent->_bf = subL->_bf = 0;}void RotateLR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;int bf = subLR->_bf;RotateL(parent->_left);RotateR(parent);if (bf == 1){parent->_bf = 0;subLR->_bf = 0;subL->_bf = -1;}else if (bf == -1){parent->_bf = 1;subLR->_bf = 0;subL->_bf = 0;}else if (bf == 0){parent->_bf = 0;subLR->_bf = 0;subL->_bf = 0;}else{assert(false);}}void RotateRL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;int bf = subRL->_bf;RotateR(parent->_right);RotateL(parent);if (bf == 1){parent->_bf = -1;subRL->_bf = 0;subR->_bf = 0;}else if (bf == -1){parent->_bf = 0;subRL->_bf = 0;subR->_bf = 1;}else if (bf == 0){parent->_bf = 0;subRL->_bf = 0;subR->_bf = 0;}else{assert(false);}}private:Node* _root = nullptr;
};
void Test_AVLTree1()
{int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };AVLTree <int, int> t1;for (auto e : a){t1.Insert(make_pair(e,e));}t1.InOrder();
}
void Test_AVLTree2()
{srand(time(0));const size_t N = 5000000;AVLTree<int, int> t;for (size_t i = 0; i < N; ++i){size_t x = rand() + i;t.Insert(make_pair(x, x));//cout << t.IsBalance() << endl;}//t.Inorder();cout << t.IsBalance() << endl;cout << t.Height() << endl;
}

2. 红黑树

2.1 红黑树的概念

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

2.2 红黑树的性质

  1. 每个节点不是红色就是黑色
  2. 根节点是黑色的
  3. 如果一个节点是红色的,那么他的两个孩子结点是黑色的
  4. 对于每个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色结点
  5. 每个叶子节点都是黑色的(NIL结点)

2.3 红黑树节点的定义

// 节点的颜色
enum Color{RED, BLACK};
// 红黑树节点的定义
template<class ValueType>
struct RBTreeNode
{RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;pair<K, V> _kv;Color _col;RBTreeNode(const pair<K, V>& kv):_left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv), _col(RED){}

我们在这里选择将默认的节点颜色给成红色,是因为插入新节点时,如果是黑色的话一定会违反上述红黑树性质的第四条,其次这个节点的插入会影响到每一条路径;如果是红的的话有可能会违反第三条,同时如果造成了影响,影响到的也只是局部。

2.4 红黑树的插入操作

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

  1. 按照二叉搜索树的规则插入新节点
   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);if (parent->_kv.first > kv.first){parent->_left = cur;}else{parent->_right = cur;}cur->_parent = parent;//TODO 调整红黑树结构return true;}
  1. 检测新节点插入以后,红黑树的性质是否遭到破坏

因为新节点的默认颜色是红色,因此如果其双亲结点的颜色是黑色,没有违反红黑树任何性质,则不需要调整;单当插入节点的双亲节点颜色为红色时,就违反了性质三不能有连续的红色节点,此时就需要对红黑树分情况讨论。
首先我们先对特殊情况进行一个分析、对红黑树的节点调整建立初步印象。
在这里插入图片描述
接下来我们对更一般的情况进行分析

  • 情况一:cur为红 ,p为红,g为黑,u存在且为红
  • 解决方法:parent 变黑 uncle变黑 grandfather变红 然后继续向上调整·
    在这里插入图片描述

在这里插入图片描述
将上面的步骤翻译为代码就是

if (uncle && uncle->_col == RED)
{parent->_col = BLACK;uncle->_col = BLACK;grandfather->_col = RED;//继续向上调整cur = grandfather;parent = cur->_parent;
}
  • 情况二: cur为红,p为红,g为黑,u不存在/u为黑(+单旋)
    在这里插入图片描述
    说明:u的情况有两种
    1、如果u结点不存在,则cur一定是新插入结点,因为如果cur不是新插入节点,则cur和p一定有一个节点的颜色是黑色,就不满足性质4:每条路经黑色节点个数相同。
    2、如果u结点存在,则其一定是黑色,那么cur结点原来的颜色一定是黑色的,现在看到其是红色的原因是因为cur的子树在调整的过程中将cur节点的颜色由黑色改为红色

p为g的左孩子,cur为p的左孩子,则进行右单旋+变色(p变黑,g变红)
p为g的右孩子,cur为p的右孩子,则进行左单旋+变色(p变黑,g变红)

  • 情况三: cur为红,p为红,g为黑,u不存在/u为黑(+双旋)

p为g的左孩子,cur为p的右孩子,p做左单旋,g右单旋+变色(p变黑,g变红)
p为g的右孩子,cur为p的左孩子,p做右单旋,g左单旋+变色(p变黑,g变红)
在这里插入图片描述
在写代码的时候需要对up之间的关系进行分类,因为这会影响到旋转的方向
将上面三种情况翻译为代码如下:


while (parent != nullptr && parent->_col == RED)
{Node* grandfather = parent->_parent;if (grandfather->_left == parent){Node* uncle = grandfather->_right;//情况1:uncle存在且为红,变色处理,并继续往上处理if (uncle && uncle->_col == RED){parent->_col = BLACK;uncle->_col = BLACK;grandfather->_col = RED;//继续向上调整cur = grandfather;parent = cur->_parent;}//情况2+3:uncle不存在/uncle存在且为黑,旋转+变色else {//      g//   p     u// cif (cur == parent->_left){RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}//      g//   p     u//       celse{RotateL(parent);RotateR(grandfather);cur->_col = BLACK;//parent->_col = RED;grandfather->_col = RED;}break;}}else//(grandfather->_right == parent){//           g//        u       p//                      cNode* uncle = grandfather->_left;//情况1:uncle存在且为红,变色处理,并继续往上处理if (uncle && uncle->_col == RED){parent->_col = BLACK;uncle->_col = BLACK;grandfather->_col = RED;//继续向上调整cur = grandfather;parent = cur->_parent;}//情况2+3:uncle不存在/uncle存在且为黑,旋转+变色else{//           g//        u       p//                      cif (cur == parent->_right){RotateL(grandfather);grandfather->_col = RED;parent->_col = BLACK;}else{//           g//        u       p//             cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}
}
_root->_col = BLACK;

2.5 红黑树的验证

红黑树的检测分为两步:

  1. 检测其是否满足二叉搜索树(中序遍历是否为有序序列)
  2. 检测其是否满足红黑树的性质(每条路径黑色节点数量相同)
bool _Check(Node* root, int blackNum, int benchmark)
{if (root == nullptr){if (benchmark != blackNum){cout << "某条路径黑色节点的数量不相等" << endl;return false;}return true;}if (root->_col == BLACK){++blackNum;}if (root->_col == RED && root->_parent && root->_parent->_col == RED){cout << "存在连续的红色节点" << endl;return false;}return _Check(root->_left, blackNum, benchmark)&& _Check(root->_right, blackNum, benchmark);
}
bool IsBalance()
{if (_root && _root->_col == RED){cout << "根节点颜色是红色" << endl;return false;}int benchmark = 0;//基准点(最左路经黑色节点的数量)Node* cur = _root;while (cur){if (cur->_col == BLACK)++benchmark;cur = cur->_left;}//连续红色结点return _Check(_root, 0, benchmark);
}

全部代码

#pragma once
#include<assert.h>
#include<iostream>
#include<set>
#include<map>
#include<string>
using namespace std;
enum Color
{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;Color _col;RBTreeNode(const pair<K, V>& kv):_left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv), _col(RED){}
};
template<class K,class V>
class RBTree
{typedef RBTreeNode<K, V> Node;
public:~RBTree(){_Destroy(_root);_root = nullptr;}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;}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);if (parent->_kv.first > kv.first){parent->_left = cur;}else{parent->_right = cur;}cur->_parent = parent;while (parent != nullptr && parent->_col == RED){Node* grandfather = parent->_parent;if (grandfather->_left == parent){Node* uncle = grandfather->_right;//情况1:uncle存在且为红,变色处理,并继续往上处理if (uncle && uncle->_col == RED){parent->_col = BLACK;uncle->_col = BLACK;grandfather->_col = RED;//继续向上调整cur = grandfather;parent = cur->_parent;}//情况2+3:uncle不存在/uncle存在且为黑,旋转+变色else {//      g//   p     u// cif (cur == parent->_left){RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}//      g//   p     u//       celse{RotateL(parent);RotateR(grandfather);cur->_col = BLACK;//parent->_col = RED;grandfather->_col = RED;}break;}}else//(grandfather->_right == parent){//           g//        u       p//                      cNode* uncle = grandfather->_left;//情况1:uncle存在且为红,变色处理,并继续往上处理if (uncle && uncle->_col == RED){parent->_col = BLACK;uncle->_col = BLACK;grandfather->_col = RED;//继续向上调整cur = grandfather;parent = cur->_parent;}//情况2+3:uncle不存在/uncle存在且为黑,旋转+变色else{//           g//        u       p//                      cif (cur == parent->_right){RotateL(grandfather);grandfather->_col = RED;parent->_col = BLACK;}else{//           g//        u       p//             cRotateR(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 && _root->_col == RED){cout << "根节点颜色是红色" << endl;return false;}int benchmark = 0;Node* cur = _root;while (cur){if (cur->_col == BLACK)++benchmark;cur = cur->_left;}//连续红色结点return _Check(_root, 0, benchmark);}int Height(){return _Height(_root);}
private:void _Destroy (Node* root){if (root == nullptr){return;}_Destroy(root->_left);_Destroy(root->_right);delete root;}int _Height(Node* root){if (root == nullptr)return 0;int leftH = _Height(root->_left);int rightH = _Height(root->_right);return leftH > rightH ? leftH + 1 : rightH + 1;}bool _Check(Node* root, int blackNum, int benchmark){if (root == nullptr){if (benchmark != blackNum){cout << "某条路径黑色节点的数量不相等" << endl;return false;}return true;}if (root->_col == BLACK){++blackNum;}if (root->_col == RED&& root->_parent&& root->_parent->_col == RED){cout << "存在连续的红色节点" << endl;return false;}return _Check(root->_left, blackNum, benchmark)&& _Check(root->_right, blackNum, benchmark);}void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);cout << root->_kv.first << " ";_InOrder(root->_right);}void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;//链接parent和subRL ,并更新三叉链parent->_right = subRL;if (subRL != nullptr)subRL->_parent = parent;Node* ppnode = parent->_parent;//链接subR和parent,并更新三叉链subR->_left = parent;parent->_parent = subR;//parent为根节点if (ppnode == nullptr){_root = subR;_root->_parent = nullptr;}//parent不为根节点else{//与ppnode链接if (ppnode->_left == parent){ppnode->_left = subR;}else{ppnode->_right = subR;}subR->_parent = ppnode;}}void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;Node* ppnode = parent->_parent;subL->_right = parent;parent->_parent = subL;parent->_left = subLR;if (subLR != nullptr)subLR->_parent = parent;if (ppnode == nullptr){_root = subL;_root->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = subL;}else{ppnode->_right = subL;}subL->_parent = ppnode;}}
private:Node* _root;
};
void Test_RBTree1()
{int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };RBTree <int, int> t1;for (auto e : a){t1.Insert(make_pair(e, e));}t1.InOrder();
}
void Test_RBTree2()
{srand(time(0));const size_t N = 5000000;RBTree<int, int> t;for (size_t i = 0; i < N; ++i){size_t x = rand() + i;t.Insert(make_pair(x, x));cout << t.IsBalance() << endl;}//t.Inorder();
}

2.6 红黑树与AVL树的比较

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

2.7 红黑树的应用

  1. C++ STL库 – ma
  2. Java 库
  3. linux内核
  4. 其他一些库

2.8 红黑树模拟实现STL中的map与set

迭代器的好处是可以方便遍历,是数据结构的底层实现与用户透明。如果想要给红黑树增加迭代器,需要考
虑以前问题:
begin()与end()
STL明确规定,begin()与end()代表的是一段前闭后开的区间,而对红黑树进行中序遍历后,可以得到一
个有序的序列,因此:begin()可以放在红黑树中最小节点(即最左侧节点)的位置,end()放在最大节点
(最右侧节点)的下一个位置,关键是最大节点的下一个位置在哪块?能否给成nullptr呢?答案是行不通
的,因为对end()位置的迭代器进行–操作,必须要能找最后一个元素,此处就不行,因此最好的方式是
将end()放在头结点的位置
在这里插入图片描述

  • operator++()与operator–()
    在这里插入图片描述
/*
template<class T, class Ref, class Ptr>
typedef __RBTreeIterator<T, Ref, Ptr> Self;
*/
Self& operator++(){if (_node->_right){// 1、右不为空,下一个就是右子树的最左节点Node* subLeft = _node->_right;while (subLeft->_left){subLeft = subLeft->_left;}_node = subLeft;}else{// 2、右为空,沿着到根的路径,找孩子是父亲左的那个祖先Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_right){cur = parent;parent = parent->_parent;}_node = parent;}return *this;}Self& operator--(){if (_node->_left){// 1、左不为空,找左子树最右节点Node* subRight = _node->_left;while (subRight->_right){subRight = subRight->_right;}_node = subRight;}else{// 2、左为空,孩子是父亲的右的那个祖先Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_left){cur = parent;parent = parent->_parent;}_node = parent;}return *this;}

3 .map和set的模拟实现

首先map和set的底层结构就是红黑树,因此在map和set中分别封装一棵红黑树,然后将其接口包装下即可。为了减少代码的冗余,我们通常会引入一个新的模版参数,来区分是map还是set的调用。详情见下图:
在这里插入图片描述

3.1 map的模拟实现

#pragma once#include "RBTree.h"namespace bit
{template<class K, class V>class map{struct MapKeyOfT{const K& operator()(const pair<const K, V>& kv){return kv.first;}};public:typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::Iterator iterator;iterator begin(){return _t.begin();}iterator end(){return _t.end();}V& operator[](const K& key){pair<iterator, bool> ret = _t.Insert(make_pair(key, V()));return ret.first->second;}pair<iterator, bool> insert(const pair<const K, V>& kv){return _t.Insert(kv);}private:RBTree<K, pair<const K, V>, MapKeyOfT> _t;};void test_map1(){map<string, string> dict;dict.insert(make_pair("sort", "排序"));dict.insert(make_pair("string", "ַ字符串"));dict.insert(make_pair("count", "计数"));dict.insert(make_pair("string", "(字符串)")); //插不进去map<string, string>::iterator it = dict.begin();while (it != dict.end()){cout << it->first << ":" << it->second << endl;/*it->first = "1111";it->second = "111";*/++it;}cout << endl;for (auto& kv : dict){cout << kv.first << ":" << kv.second << endl;}cout << endl;}void test_map2(){string arr[] = { "你", "今", "天", "真", "好", "看", };map<string, int> countMap;for (auto& e : arr){countMap[e]++;}for (auto& kv : countMap){cout << kv.first << ":" << kv.second << endl;}}
}

3.2 set的模拟实现

#pragma once
#include"RBTree.h"
namespace Maria
{template <class K>class set{struct SetKeyOfT{const K& operator()(const K& key){return key;}};public: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);}private:RBTree < K,K,SetKeyOfT> _t;};void test_set(){set<int> s;s.Insert(3);s.Insert(1);s.Insert(2);set<int>::iterator it = s.begin();while (it != s.end()){cout << *it << " ";++it;}cout << endl;}
}

3.3 改造红黑树

#pragma once
#include<iostream>
using namespace std;enum Colour
{RED,BLACK,
};template<class T>
struct RBTreeNode
{RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;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){}__RBTreeIterator(const __RBTreeIterator<T, T&, T*>& it):_node(it._node){}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}bool operator!=(const Self& s){return _node != s._node;}Self& operator--(){if (_node->_left){// 1、左不为空,找左子树最右节点Node* subRight = _node->_left;while (subRight->_right){subRight = subRight->_right;}_node = subRight;}else{// 2、左为空,孩子是父亲的右的那个祖先Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_left){cur = parent;parent = parent->_parent;}_node = parent;}return *this;}Self& operator++(){if (_node->_right){// 1、右不为空,下一个就是右子树的最左节点Node* subLeft = _node->_right;while (subLeft->_left){subLeft = subLeft->_left;}_node = subLeft;}else{//2、右为空、沿着到根的路径,找孩子是父亲左的那个祖先Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_right){cur = parent;parent = parent->_parent;}_node = parent;}return *this;}
};// 仿函数
template<class K, class T, class KeyOfT>
class RBTree
{typedef RBTreeNode<T> Node;
public:~RBTree(){_Destroy(_root);_root = nullptr;}
public:typedef __RBTreeIterator<T, T&, T*> Iterator;typedef __RBTreeIterator<T, const T&, const T*> const_Iterator;Iterator begin(){Node* cur = _root;while (cur && cur->_left){cur = cur->_left;}return Iterator(cur);}Iterator end(){return Iterator(nullptr);}Node* Find(const K& key){Node* cur = _root;KeyOfT kot;while (cur){if (kot(cur->_data) < key){cur = cur->_right;}else if (kot(cur->_data) > key){cur = cur->_left;}else{return cur;}}return nullptr;}pair<Iterator,bool>  Insert(const T& data){if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;return make_pair(Iterator(_root),true);}KeyOfT kot;Node* parent = nullptr;Node* 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),false );}}cur = new Node(data);Node* newnode = cur;if (kot(parent->_data) > kot(data)){parent->_left = cur;}else{parent->_right = cur;}cur->_parent = parent;while (parent && parent->_col == RED){Node* grandfather = parent->_parent;if (grandfather->_left == parent){Node* uncle = grandfather->_right;// 情况1:u存在且为红,变色处理,并继续往上处理if (uncle && uncle->_col == RED){parent->_col = BLACK;uncle->_col = BLACK;grandfather->_col = RED;// 继续往上调整cur = grandfather;parent = cur->_parent;}else // 情况2+3:u不存在/u存在且为黑,旋转+变色{//     g//   p   u// c if (cur == parent->_left){RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//     g//   p   u//     cRotateL(parent);RotateR(grandfather);cur->_col = BLACK;//parent->_col = RED;grandfather->_col = RED;}break;}}else // (grandfather->_right == parent){//    g//  u   p//        cNode* uncle = grandfather->_left;// 情况1:u存在且为红,变色处理,并继续往上处理if (uncle && uncle->_col == RED){parent->_col = BLACK;uncle->_col = BLACK;grandfather->_col = RED;// 继续往上调整cur = grandfather;parent = cur->_parent;}else // 情况2+3:u不存在/u存在且为黑,旋转+变色{//    g//  u   p//        cif (cur == parent->_right){RotateL(grandfather);grandfather->_col = RED;parent->_col = BLACK;}else{//    g//  u   p//    cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return make_pair(Iterator(newnode), true);}bool IsBalance(){if (_root && _root->_col == RED){cout << "根节点颜色是红色" << endl;return false;}int benchmark = 0;Node* cur = _root;while (cur){if (cur->_col == BLACK)++benchmark;cur = cur->_left;}// 连续红色节点return _Check(_root, 0, benchmark);}int Height(){return _Height(_root);}private:void _Destroy(Node* root){if (root == nullptr){return;}_Destroy(root->_left);_Destroy(root->_right);delete root;}int _Height(Node* root){if (root == NULL)return 0;int leftH = _Height(root->_left);int rightH = _Height(root->_right);return leftH > rightH ? leftH + 1 : rightH + 1;}bool _Check(Node* root, int blackNum, int benchmark){if (root == nullptr){if (benchmark != blackNum){cout << "某条路径黑色节点的数量不相等" << endl;return false;}return true;}if (root->_col == BLACK){++blackNum;}if (root->_col == RED&& root->_parent&& root->_parent->_col == RED){cout << "存在连续的红色节点" << endl;return false;}return _Check(root->_left, blackNum, benchmark)&& _Check(root->_right, blackNum, benchmark);}void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;Node* ppnode = parent->_parent;subR->_left = parent;parent->_parent = subR;if (ppnode == nullptr){_root = subR;_root->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = subR;}else{ppnode->_right = subR;}subR->_parent = ppnode;}}void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;Node* ppnode = parent->_parent;subL->_right = 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;}}private:Node* _root = nullptr;
};

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/13670.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Latex | 使用MATLAB生成.eps矢量图并导入Latex中的方法

一、问题描述 用Latex时写paper时&#xff0c;要导入MATLAB生成的图进去 二、解决思路 &#xff08;1&#xff09;在MATLAB生成图片的窗口中&#xff0c;导出.eps矢量图 &#xff08;2&#xff09;把图上传到overleaf的目录 &#xff08;3&#xff09;在文中添加相应代码 三…

搜索与图论(一)

一、DFS与BFS 1.1深度优先搜索(DFS) DFS不具有最短性 //排列数字问题 #include<iostream> using namespace std;const int N 10; int n; int path[N]; bool st[N];void dfs(int u) {if(u n){for(int i 0;i < n;i) printf("%d",path[i]);puts("&qu…

15、PHP神奇的数组索引替代

1、有数字索引指定的数组元素时&#xff0c;以数字索引的为准。 <?php $aarray(a,b,1>c,5>"d","e"); print_r($a); ?> 输出结果&#xff1a;b的位置直接被c替代了&#xff0c;e 的值为最大的整数索引1。 PHP不这么搞&#xff0c;怎么可能成…

【信号去噪和正交采样】流水线过程的一部分,用于对L波段次级雷达中接收的信号进行降噪(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

简单学会MyBatis原生API注解

&#x1f600;前言 本篇博文是关于MyBatis原生API&注解的使用&#xff0c;希望能够帮助到你&#x1f60a; &#x1f3e0;个人主页&#xff1a;晨犀主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是晨犀&#xff0c;希望我的文章可以帮助到大家&#xff0c;您…

VUE使用docxtemplater导出word(带图片) 踩坑 表格循环空格 ,canvas.toDataURL图片失真模糊问题

参考&#xff1a;https://www.codetd.com/article/15219743 安装 // 安装 docxtemplater npm install docxtemplater pizzip --save // 安装 jszip-utils npm install jszip-utils --save // 安装 jszip npm install jszip --save // 安装 FileSaver npm install file-save…

【力扣每日一题】2023.7.29 环形链表

目录 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 代码&#xff1a; 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 题目给我们一个链表&#xff0c;让我们判断这个链表是否有环。我们可以直接遍历这个链表&#xff0c;最后能走到链表末尾也就是空指针那就…

VMware虚拟机无法上网的解决办法

&#xff08;1&#xff09;1、在虚拟机右下角的网络适配器上面观察该图标是否是有绿色的灯在闪烁&#xff0c;如果网络适配器是灰色的证明虚拟机的网络没有打开&#xff0c;而是被禁用了&#xff0c;在适配器上点击鼠标右键&#xff0c;打开【设置】&#xff0c;在【已连接】、…

数据结构—链表

链表 前言链表链表的概念及结构链表的分类 无头单向非循环链表的相关实现带头双向循环链表的相关实现顺序表和链表&#xff08;带头双向循环链表&#xff09;的区别 前言 顺序表是存在一些固有的缺陷的&#xff1a; 中间/头部的插入删除&#xff0c;时间复杂度为O(N)&#xf…

windows C++多线程同步<2>-事件

windows C多线程同步&#xff1c;2&#xff1e;-事件 事件对象和关键代码段不同&#xff0c;它是属于内核对象&#xff1b;又分为人工重置事件对象和自动重置事件对象&#xff1b; 同一个线程不允许在不释放事件的情况下多次获取事件&#xff1b; 相关API 白话来讲&#xff1…

认识 springboot 并了解它的创建过程 - 1

前言 本篇介绍什么是SpringBoot, SpringBoot项目如何创建&#xff0c;认识创建SpringBoot项目的目录&#xff0c;了解SpringBoo特点如有错误&#xff0c;请在评论区指正&#xff0c;让我们一起交流&#xff0c;共同进步&#xff01; 文章目录 前言1.什么是springboot?2.为什么…

PKG内容查看工具:Suspicious Package for Mac安装教程

Suspicious Package Mac版是一款Mac平台上的查看 PKG 程序包内信息的应用&#xff0c;Suspicious Package Mac版支持查看全部包内全部文件&#xff0c;比如需要运行的脚本&#xff0c;开发者&#xff0c;来源等等。 suspicious package mac使用简单&#xff0c;只需在选择pkg安…

农业中的计算机视觉 2023

物体检测应用于检测田间收割机和果园苹果 一、说明 欢迎来到Voxel51的计算机视觉行业聚焦博客系列的第一期。每个月&#xff0c;我们都将重点介绍不同行业&#xff08;从建筑到气候技术&#xff0c;从零售到机器人等&#xff09;如何使用计算机视觉、机器学习和人工智能来推动…

网络安全-防御需知

目录 网络安全-防御 1.网络安全常识及术语 资产 漏洞 0day 1day 后门 exploit APT 2.什么会出现网络安全问题&#xff1f; 网络环境的开放性 协议栈自身的脆弱性 操作系统自身的漏洞 人为原因 客观原因 硬件原因 缓冲区溢出攻击 缓冲区溢出攻击原理 其他攻击…

网络安全行业相关证书

一&#xff1a;前言 对于考证这个话题&#xff0c;笔者的意见是&#xff1a;“有比没有好&#xff0c;有一定更好&#xff0c;但不一定必须&#xff1b;纸上证明终觉浅&#xff0c;安全还得实力行”。很多人对于各种机构的考证宣传搞得是云里雾里&#xff0c;不知道网络安全行业…

flex盒子 center排布,有滚动条时,拖动滚动条无法完整显示内容

文章目录 问题示例代码解决问题改进后的效果 问题 最近在开发项目的过程中&#xff0c;发现了一个有趣的事情&#xff0c;与flex盒子有关&#xff0c;不知道算不算是一个bug&#xff0c;不过对于开发者来说&#xff0c;确实有些不方便&#xff0c;感兴趣的同学不妨也去试试。 …

设计模式-建造者模式

在前面几篇文章中&#xff0c;已经讲解了单例模式、工厂方法模式、抽象工厂模式&#xff0c;创建型还剩下一个比较重要的模式-建造者模式。在理解该模式之前&#xff0c;我还是希望重申设计模式的初衷&#xff0c;即为解决一些问题而提供的优良方案。学习设计模式遗忘其初衷&am…

VLAN原理(Virtual LAN 虚拟局域网)

VLAN&#xff08;Virtual LAN 虚拟局域网&#xff09; 1、广播/广播域 2、广播的危害&#xff1a;增加网络/终端负担&#xff0c;传播病毒&#xff0c; 3、如何控制广播&#xff1f;&#xff1f; ​ 控制广播隔离广播域 ​ 路由器物理隔离广播 ​ 路由器隔离广播缺点&…

解决在云服务器开放端口号以后telnet还是无法连接的问题

这里用阿里云服务器举例&#xff0c;在安全组开放了对应的TCP端口以后。使用windows的cmd下的telnet命令&#xff0c;还是无法正常连接。 telnet IP地址 端口号解决方法1&#xff1a; 在轻量服务器控制台的防火墙规则中添加放行端口。 阿里云-管理防火墙 如图&#xff0c;开放…

右击不显示TortoiseGit图标处理方法

第一种 右键--》TortoiseGIt--》setting--》Icon Overlays--》Status cache&#xff0c;按照下图设置&#xff0c;然后重启电脑。 第二种 进入注册信息&#xff0c;按照步骤找到HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIden…