【C++】用红黑树封装map、set

用红黑树封装map、set

  • 1. 红黑树
    • 1.1 模板参数的控制
      • 1.1.1 Value
      • 1.1.2 KeyOfValue
    • 1.2 正向迭代器
      • 1.2.1 构造函数
      • 1.2.2 begin()+end()
      • 1.2.3 operator++()
      • 1.2.4 operator--()
      • 1.2.5 operator*()
      • 1.2.6 operator->()
      • 1.2.7 operator==()
      • 1.2.8 operator!=()
      • 1.2.9 总代码
    • 1.3 反向迭代器
      • 1.3.1 rbegin()+rend()
      • 1.3.2 总代码
    • 1.4 find()
    • 1.5 insert()
  • 2. Map
    • 2.1 operator[]
  • 3. typename作用
  • 4. 完整代码
    • 4.1 Map.h
    • 4.2 Set.h
    • 4.3 RBTree.h

1. 红黑树

  • set是K模型,map是KV模型,二者底层都是使用红黑树来实现的,所以我们可以将红黑树设置为模板,即:set、map复用同一个类模板的红黑树。

1.1 模板参数的控制

1.1.1 Value

  • Value决定你是k模型的set、还是KV模型的map。

map、set的模板参数value.png

enum Color {  //枚举,一一列举出事物具有的所有可能Red,  //枚举常量,给枚举变量进行赋值Black,
};template<class T>//红黑树的节点
struct RBTreeNode {typedef RBTreeNode<T> Node;//三叉链-》优点:便于查找孩子、父亲节点Node* _left;      //该节点的左孩子Node* _right;    //该节点的右孩子Node* _parent;  //该节点的父亲,便于向上更新T _data;Color _col;RBTreeNode(const T& data, Color col = Red)  //构造函数:_data(data), _left(nullptr), _right(nullptr), _parent(nullptr), _col(col)  //默认新插入节点的颜色为红色{ }
};
//Value决定你是k模型的set、还是KV模型的map
template<class K, class T, class KeyOfT> 
class RBTree {  
public:typedef RBTreeNode<T> Node;
};
template<class K>
class set{   //K模型
public:   private:  //set中的key不允许被修改RBTree<K, const K, SetKeyOfT> _t;  //红黑树对象};
}
template<class K, class V>
class map {   //KV模型  
public:private:   //map中的key不允许被修改RBTree<K, pair<const K, V>, MapKeyOfT> _t;  //红黑树对象};};

1.1.2 KeyOfValue

  • KeyOfT : 取出Value对象中的key。

image.png

// KeyOfT : 取出Value对象中的key
template<class K, class T, class KeyOfT> 
class RBTree {  };
struct SetKeyOfT{   const K& operator()(const K& key){return key;  //key}
};
struct MapKeyOfT {const K& operator()(const pair<K, V>& kv){return kv.first;  //pair中的key}
};

1.2 正向迭代器

1.2.1 构造函数

💡RBTreeIterator(Node* node) ;

RBTreeIterator(Node* node) //构造函数,单参数构造函数支持隐式类型转化:_node(node){ }
  • Tips : 单参数构造函数支持隐式类型转换 Node*->iterator 。

1.2.2 begin()+end()

💡iterator begin( ) ;

  • 功能:返回红黑树中最左节点(左孩子必为空)的迭代器。
  • Tips:set、map对象为非const对象,就调用begin()、end()。
iterator begin()  //红黑树最左节点
{ Node* subLeft = _root;   while (subLeft && subLeft->_left)subLeft = subLeft->_left;return iterator(subLeft);
}	

💡iterator end( ) ;

  • 功能:返回最后一个元素的下一个的迭代器(空指针)。
iterator end()  //空指针 左闭右开[begin,end)
{return iterator(nullptr);
}

image.png

1.2.3 operator++()

/*1.右不为空,下一个为该节点右子树的最左节点 ;* 2.右为空,说明该节点所在的子树已经访问完了,若该节点为父亲的右,说明该父亲所在的子树也访问完了,继续往上找,* 直到该节点为父亲的左,则要访问的下一个节点是它的父亲,即:找祖先里面的孩纸 = 父亲左*/
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 && parent->_left != cur){cur = parent;parent = cur->_parent;}_node = parent;}return *this;
}
  • 中序遍历,左、根、右。
    情况一:右不为空,下一个为该节点右子树的最左节点 ;
    情况二:右为空,说明该节点所在的子树已经访问完了,若该节点为父亲的右,说明该父亲所在的子树也访问完了,继续往上找,直到该节点为父亲的左,则要访问的下一个节点是它的父亲,即:找祖先里面的孩纸 = 父亲左。

image.png

1.2.4 operator–()

/*1.左不为空,下一个为该节点左子树的最右节点 ;* 2.左为空,说明该节点所在的子树已经访问完了,若该节点为父亲的左,说明该父亲所在的子树也访问完了,继续往上找,* 直到该节点为父亲的右,则要访问的下一个节点是它的父亲,即:找祖先里面的孩纸 = 父亲右*/
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 && parent->_right != cur){cur = parent;parent = cur->_parent;}_node = parent;}return *this;
}

image.png

1.2.5 operator*()

Ref operator*()
{return _node->_data;
}

1.2.6 operator->()

Ptr operator->() //结构体指针,data为结构体
{return &_node->_data;
}

1.2.7 operator==()

bool operator==(const Self& rb)
{return _node == rb._node;
}

1.2.8 operator!=()

bool operator!=(const Self& rb)
{return _node != rb._node;
}

1.2.9 总代码

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->() //结构体指针,data为结构体{return &_node->_data;}/*1.右不为空,下一个为该节点右子树的最左节点 ;* 2.右为空,说明该节点所在的子树已经访问完了,若该节点为父亲的右,说明该父亲所在的子树也访问完了,继续往上找,* 直到该节点为父亲的左,则要访问的下一个节点是它的父亲,即:找祖先里面的孩纸 = 父亲左*/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 && parent->_left != cur){cur = parent;parent = cur->_parent;}_node = parent;}return *this;}/*1.左不为空,下一个为该节点左子树的最右节点 ;* 2.左为空,说明该节点所在的子树已经访问完了,若该节点为父亲的左,说明该父亲所在的子树也访问完了,继续往上找,* 直到该节点为父亲的右,则要访问的下一个节点是它的父亲,即:找祖先里面的孩纸 = 父亲右*/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 && parent->_right != cur){cur = parent;parent = cur->_parent;}_node = parent;}return *this;}bool operator!=(const Self& rb){return _node != rb._node;}bool operator==(const Self& rb){return _node == rb._node;}
};

1.3 反向迭代器

1.3.1 rbegin()+rend()

💡reverse_iterator rbegin( ) ;

  • 功能:返回红黑树中最右节点(右孩子必为空)的迭代器。
reverse_iterator rbegin()  //红黑树最右节点,因为此处反向迭代器*,是直接调用正向迭代器* [rbegin,erend)
{Node* subRight = _root;while (subRight && subRight->_right)subRight = subRight->_right;return reverse_iterator(subRight);
}

💡reverse_iterator rend( ) ;

  • 功能:返回第一个元素的前一个的迭代器(空指针)。
reverse_iterator rend()  
{return reverse_iterator(nullptr);
}

1.3.2 总代码

template<class iterator, class Ref, class Ptr>
struct ReverseIterator  //红黑树的反向迭代器——适配器 begin = rend、end = rbegin
{typedef ReverseIterator<iterator, Ref, Ptr> Self;  iterator _it;   //适配器ReverseIterator(iterator it):_it(it){ }Ref operator*(){return *_it;}Ptr operator->(){return &(operator*());  //}Self& operator++(){--_it;return *this;}Self& operator--(){++_it;return *this;}bool operator==(const Self& rb){return _it == rb._it;}bool operator!=(const Self& rb){return _it != rb._it;}
};

1.4 find()

💡iterator find(const K& key) ;

  • 功能:查找。
  • 若key在红黑树中,则返回树中与key值相等元素的迭代器,否则,就返回end( )。
iterator find(const K& key)  //查找  模板参数K的作用
{KeyOfT kot;Node* cur = _root;while (cur)  //先按照二叉搜索树的方式插入{if (kot(cur->_data) < key)  //通仿函数对象调用operator()来获取T中的keycur = cur->_right;else if (kot(cur->_data) > key)cur = cur->_left;elsereturn iterator(cur);  //找到了}return end();  //找不到
}

1.5 insert()

💡pair<iterator,bool> insert(const T& data) ;

  • 功能:向红黑树中插入data。

image.png

  • insert返回值为pair<iterator, bool>,若key(set的key、map的pair的first)在树中存在,因为搜索树中不能出现重复的键值key,所以pair::first指向在树中与key值相等的迭代器,pair::second为false。若key在树中不存在,pair::first指向在树中新插入元素的迭代器,pair::second为true。insert相当于查找。
pair<iterator, bool> insert(const T& data)  //插入  
{   //不能使用引用放回,因为返回值作用域为栈区,传值返回KeyOfT kot;  //仿函数类创建的对象,对象去调用operator()Node* parent = nullptr;Node* cur = _root;while (cur)  //先按照二叉搜索树的方式插入{if (kot(cur->_data) < kot(data))  //通仿函数对象调用operator()来获取T中的key值{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);  //注意 insertNode* newnode = cur;  //非空树,插入成功,因为要做旋转处理,会导致cur值发生改变,需提前将新节点的位置存储起来if (parent == nullptr) //空树{_root = cur;_root->_col = Black;  //跟节点为黑return make_pair(iterator(_root), true);  //空树,插入成功,返回新插入节点在树中的迭代器}if (kot(parent->_data) < kot(cur->_data)){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;  //记录当前节点的父亲//更新颜色//插入结束:1.插入节点的父亲是黑色,因为插入前该树就为红黑树 2.情况一处理完后,cur为根节点,且为黑色while (parent && parent->_col == Red){ //爷爷一定存在,因为c为红,p为红,所以p一定不是根节点,且一定有父节点Node* grandfather = parent->_parent;if (parent == grandfather->_left)  //旋转需要确定方向{Node* uncle = grandfather->_right;if (uncle && uncle->_col == Red) //情况一:叔叔存在且为红->无方向(p、u为g的任意边,c为p的任一边){  //cur可能为新增节点,也可能一开始为黑色,cur的子树(下一层为红,下一层为新插入节点)在调整过程中将cur由黑变为红parent->_col = uncle->_col = Black; //p、u变为黑,g变为红grandfather->_col = Red;//g可能为根节点(更新结束),也可能为子树(继续向上更新)cur = grandfather;parent = cur->_parent;}else  //情况二:叔叔不存在 或者 叔叔存在且为黑{  //叔叔不存在,cur为新增节点 或 cur原来为黑,经子树调整由黑变红if (parent->_left == cur)  //左左——右单旋{RotateR(grandfather);parent->_col = Black; //p变为黑,g变为红grandfather->_col = Red;}else    //左右——左右单旋 {RotateL(parent);RotateR(grandfather);cur->_col = Black;  //c变黑,g变红grandfather->_col = Red;}break;  //更新结束:3.旋转+颜色处理后就是红黑树了}}else{Node* uncle = grandfather->_left;if (uncle && uncle->_col == Red){parent->_col = uncle->_col = Black;grandfather->_col = Red;cur = grandfather;parent = cur->_parent;}else{if (parent->_right == cur)  //右右——左单旋{RotateL(grandfather);parent->_col = Black;grandfather->_col = Red;}else   //右左——右左单旋{RotateR(parent);RotateL(grandfather);cur->_col = Black;grandfather->_col = Red;}break;}}}_root->_col = Black;  //g为根,颜色变为黑,更新结束return make_pair(iterator(newnode), true);  //情况一,插入节点的父亲为黑,插入结束
}

2. Map

2.1 operator[]

💡V& operator[ ](const K& key) ;

  • 功能:访问与key相对应的value值。即可读又可写。
  • 原理:operator[ ]底层是通过调用insert( )将键值队插入到map中。如果key存在,插入失败,insert返回与map中key值相同元素的迭代器。如果key不存在,插入成功,insert返回在map中新插入元素的迭代器。operator[ ]最后返回与key值相对应的value值的引用。
  • operator[ ] 具有插入、查找、插入+修改、查找+修改功能。
V& operator[](const K& key) //功能:查找+修改、插入+修改
{pair<iterator, bool> ret = _t.insert(make_pair(key, V()));return ret.first->second;
}

3. typename作用

  1. 使用域作用限定符(: : )的两种情况:静态变量、类中typedef的类型。
  2. 使用typename表示: :后面为类型,不是静态成员
//使用::两种情况:静态变量、类中typedef的类型  typename表示::前面为类型,不是静态成员
typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::iterator iterator; 

4. 完整代码

4.1 Map.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1#include"RBTree.h"
#include<string>namespace zzx {template<class K, class V>class map {   //KV模型  public:struct MapKeyOfT {const K& operator()(const pair<K, V>& kv){return kv.first;  //pair中的key}};//使用::两种情况:静态变量、类中typedef的类型  typename表示::前面为类型,不是静态成员typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::iterator iterator;   //正向迭代器typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::reverse_iterator reverse_iterator;  //反向迭代器iterator begin()  {return _t.begin();}iterator end()  {return _t.end();}reverse_iterator rbegin(){return _t.rbegin();}reverse_iterator rend(){return _t.rend();}iterator find(const K& key)  //查找{return _t.find(key);}pair<iterator, bool> insert(pair<const K, V>& kv)  //插入{return _t.insert(kv);}V& operator[](const K& key) //功能:查找+修改、插入+修改{pair<iterator, bool> ret = _t.insert(make_pair(key, V()));return ret.first->second;}private:   //map中的key不允许被修改RBTree<K, pair<const K, V>, MapKeyOfT> _t;  //红黑树对象};};

4.2 Set.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1#include"RBTree.h"namespace zzx{template<class K>class set{   //K模型public:   //仿函数类:比较对象大小,获取对象中的元素—自己手动传递比较逻辑struct SetKeyOfT{   const K& operator()(const K& key){return key;  //key}};//使用::两种情况:静态变量、类中typedef的类型  typename表示::后面为类型,不是静态成员typedef typename RBTree<K, const K, SetKeyOfT>::iterator iterator;   //正向迭代器typedef typename RBTree<K, const K, SetKeyOfT>::reverse_iterator reverse_iterator;  //反向迭代器iterator begin(){return _t.begin();}iterator end(){return _t.end();}reverse_iterator rbegin(){return _t.rbegin();}reverse_iterator rend(){return _t.rend();}iterator find(const K& key)  //查找{return _t.find(key);}pair<iterator, bool> insert(const K& key)  //插入{return _t.insert(key);}private:  //set中的key不允许被修改RBTree<K, const K, SetKeyOfT> _t;  //红黑树对象};
}

4.3 RBTree.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>using namespace std;enum Color {  //枚举,一一列举出事物具有的所有可能Red,  //枚举常量,给枚举变量进行赋值Black,
};template<class T>//红黑树的节点
struct RBTreeNode {typedef RBTreeNode<T> Node;//三叉链-》优点:便于查找孩子、父亲节点Node* _left;      //该节点的左孩子Node* _right;    //该节点的右孩子Node* _parent;  //该节点的父亲,便于向上更新T _data;Color _col;RBTreeNode(const T& data, Color col = Red)  //构造函数:_data(data), _left(nullptr), _right(nullptr), _parent(nullptr), _col(col)  //默认新插入节点的颜色为红色{ }
};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->() //结构体指针,data为结构体{return &_node->_data;}/*1.右不为空,下一个为该节点右子树的最左节点 ;* 2.右为空,说明该节点所在的子树已经访问完了,若该节点为父亲的右,说明该父亲所在的子树也访问完了,继续往上找,* 直到该节点为父亲的左,则要访问的下一个节点是它的父亲,即:找祖先里面的孩纸 = 父亲左*/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 && parent->_left != cur){cur = parent;parent = cur->_parent;}_node = parent;}return *this;}/*1.左不为空,下一个为该节点左子树的最右节点 ;* 2.左为空,说明该节点所在的子树已经访问完了,若该节点为父亲的左,说明该父亲所在的子树也访问完了,继续往上找,* 直到该节点为父亲的右,则要访问的下一个节点是它的父亲,即:找祖先里面的孩纸 = 父亲右*/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 && parent->_right != cur){cur = parent;parent = cur->_parent;}_node = parent;}return *this;}bool operator!=(const Self& rb){return _node != rb._node;}bool operator==(const Self& rb){return _node == rb._node;}
};template<class iterator, class Ref, class Ptr>
struct ReverseIterator  //红黑树的反向迭代器——适配器 begin = rend、end = rbegin
{typedef ReverseIterator<iterator, Ref, Ptr> Self;  iterator _it;   //适配器ReverseIterator(iterator it):_it(it){ }Ref operator*(){return *_it;}Ptr operator->(){return &(operator*());  //}Self& operator++(){--_it;return *this;}Self& operator--(){++_it;return *this;}bool operator==(const Self& rb){return _it == rb._it;}bool operator!=(const Self& rb){return _it != rb._it;}
};//红黑树的模板参数:T决定你是k模型的set、还是KV模型的map ; KeyOfT:取出T对象中的key ; pair比较:先比较first,在比较second
template<class K, class T, class KeyOfT> 
class RBTree {  
public:typedef RBTreeNode<T> Node;//正向迭代器typedef RBTreeIterator<T, T&, T*> iterator;  //普通迭代器typedef RBTreeIterator<T,const T&, const T*> const_iterator; //const迭代器//反向迭代器typedef ReverseIterator<iterator, T&, T*> reverse_iterator;typedef ReverseIterator<const_iterator, const T&, const T*> const_reverse_iterator;iterator begin()  //红黑树最左节点{ Node* subLeft = _root;   while (subLeft && subLeft->_left)subLeft = subLeft->_left;return iterator(subLeft);}iterator end()  //空指针 左闭右开[begin,end){return iterator(nullptr);}const_iterator begin()const  {Node* subLeft = _root;while (subLeft && subLeft->_left)subLeft = subLeft->_left;return const_iterator(subLeft);}const_iterator end()const{return const_iterator(nullptr);}reverse_iterator rbegin()  //红黑树最右节点,因为此处反向迭代器*,是直接调用正向迭代器* [rbegin,erend){Node* subRight = _root;while (subRight && subRight->_right)subRight = subRight->_right;return reverse_iterator(subRight);}reverse_iterator rend()  {return reverse_iterator(nullptr);}const_reverse_iterator rbegin()const{Node* subRight = _root;while (subRight && subRight->_right)subRight = subRight->_right;return const_reverse_iterator(subRight);}const_reverse_iterator rend()const{return const_reverse_iterator(nullptr);}iterator find(const K& key)  //查找  模板参数K的作用{KeyOfT kot;Node* cur = _root;while (cur)  //先按照二叉搜索树的方式插入{if (kot(cur->_data) < key)  //通仿函数对象调用operator()来获取T中的keycur = cur->_right;else if (kot(cur->_data) > key)cur = cur->_left;elsereturn iterator(cur);  //找到了}return end();  //找不到}void RotateL(Node* parent)  //右右—左单旋{Node* subR = parent->_right;Node* subRL = subR->_left;Node* pphead = parent->_parent;parent->_right = subRL;if (subRL)subRL->_parent = parent;subR->_left = parent;parent->_parent = subR;if (parent == _root){_root = subR;subR->_parent = nullptr;}else{if (pphead->_left == parent)pphead->_left = subR;elsepphead->_right = subR;subR->_parent = pphead;}}void RotateR(Node* parent)  //左左—右单旋{Node* subL = parent->_left;Node* subLR = subL->_right;Node* pphead = parent->_parent;parent->_left = subLR;if (subLR)subLR->_parent = parent;subL->_right = parent;parent->_parent = subL;if (parent == _root){_root = subL;subL->_parent = nullptr;}else{if (pphead->_left == parent)pphead->_left = subL;elsepphead->_right = subL;subL->_parent = pphead;}}void RotateRL(Node* parent)  //右左—先右旋再左旋{Node* subR = parent->_right;Node* subRL = subR->_left;RotateR(subR);RotateL(parent);}void RotateLR(Node* parent)  //左右—先左旋再右旋{Node* subL = parent->_left;Node* subLR = subL->_right;RotateL(subL);RotateR(parent);}pair<iterator, bool> insert(const T& data)  //插入  {   //不能使用引用放回,因为返回值作用域为栈区,传值返回KeyOfT kot;  //仿函数类创建的对象,对象去调用operator()Node* parent = nullptr;Node* cur = _root;while (cur)  //先按照二叉搜索树的方式插入{if (kot(cur->_data) < kot(data))  //通仿函数对象调用operator()来获取T中的key值{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);  //注意 insertNode* newnode = cur;  //非空树,插入成功,因为要做旋转处理,会导致cur值发生改变,需提前将新节点的位置存储起来if (parent == nullptr) //空树{_root = cur;_root->_col = Black;  //跟节点为黑return make_pair(iterator(_root), true);  //空树,插入成功,返回新插入节点在树中的迭代器}if (kot(parent->_data) < kot(cur->_data)){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;  //记录当前节点的父亲//更新颜色//插入结束:1.插入节点的父亲是黑色,因为插入前该树就为红黑树 2.情况一处理完后,cur为根节点,且为黑色while (parent && parent->_col == Red){ //爷爷一定存在,因为c为红,p为红,所以p一定不是根节点,且一定有父节点Node* grandfather = parent->_parent;if (parent == grandfather->_left)  //旋转需要确定方向{Node* uncle = grandfather->_right;if (uncle && uncle->_col == Red) //情况一:叔叔存在且为红->无方向(p、u为g的任意边,c为p的任一边){  //cur可能为新增节点,也可能一开始为黑色,cur的子树(下一层为红,下一层为新插入节点)在调整过程中将cur由黑变为红parent->_col = uncle->_col = Black; //p、u变为黑,g变为红grandfather->_col = Red;//g可能为根节点(更新结束),也可能为子树(继续向上更新)cur = grandfather;parent = cur->_parent;}else  //情况二:叔叔不存在 或者 叔叔存在且为黑{  //叔叔不存在,cur为新增节点 或 cur原来为黑,经子树调整由黑变红if (parent->_left == cur)  //左左——右单旋{RotateR(grandfather);parent->_col = Black; //p变为黑,g变为红grandfather->_col = Red;}else    //左右——左右单旋 {RotateL(parent);RotateR(grandfather);cur->_col = Black;  //c变黑,g变红grandfather->_col = Red;}break;  //更新结束:3.旋转+颜色处理后就是红黑树了}}else{Node* uncle = grandfather->_left;if (uncle && uncle->_col == Red){parent->_col = uncle->_col = Black;grandfather->_col = Red;cur = grandfather;parent = cur->_parent;}else{if (parent->_right == cur)  //右右——左单旋{RotateL(grandfather);parent->_col = Black;grandfather->_col = Red;}else   //右左——右左单旋{RotateR(parent);RotateL(grandfather);cur->_col = Black;grandfather->_col = Red;}break;}}}_root->_col = Black;  //g为根,颜色变为黑,更新结束return make_pair(iterator(newnode), true);  //情况一,插入节点的父亲为黑,插入结束}private:Node* _root = nullptr;
};

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

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

相关文章

规则引擎-Easy rule

规则引擎-Easy rule 最近有几个项目中都出现了根据XX条件执行XX方法的业务&#xff0c;在动手之前脑中总会下意识的发现如果按照常规的去写代码&#xff0c;无论使用何种设计模式&#xff0c;都会出现不同程度上的代码冗余或大量if-else判断。 甚至说判断XX条件的代码和执行X…

2024中国大学计算机科学与技术专业排名(非常详细)零基础入门到精通,收藏这一篇就够了

今天 6 月 8 号&#xff0c;高考第二天&#xff0c;随着大部分地区的高考结束&#xff0c;接下来就是焦急的等待分数的公布&#xff0c;然后学生们就迎来人生中的一个重要时刻——选择大学和专业。 在众多专业中&#xff0c;计算机科学与技术一直是备受瞩目的热门选择&#xf…

【爬虫】使用Python爬取百度学术页面的标题、作者、摘要和关键词

目录 安装所需库编写爬虫代码解释运行脚本结果 在本文中&#xff0c;我将介绍如何使用Python编写一个网络爬虫&#xff0c;从百度学术页面提取研究论文的标题、作者、摘要和关键词。我们将使用 requests和 BeautifulSoup库来实现这一目标。 安装所需库 首先&#xff0c;确保…

uniapp引入uview无代码提示

前提安装正确&#xff1a; 无论是基于npm和Hbuilder X方式安装&#xff0c;一定要配置正确。 解决办法 以前在pages.json里面的写法&#xff1a; "easycom": {"^u-(.*)": "uview-ui/components/u-$1/u-$1.vue" }但是现在hbuilderx要求规范ea…

驱动开发之 input 子系统

1.input 子系统介绍 input 就是输入的意思&#xff0c;input 子系统就是管理输入的子系统&#xff0c;和 pinctrl、gpio 子系统 一样&#xff0c;都是 Linux 内核针对某一类设备而创建的框架。比如按键输入、键盘、鼠标、触摸屏等 等这些都属于输入设备&#xff0c;不同的输入…

Redis到底支不支持事务?

文章目录 一、概述二、使用1、正常执行&#xff1a;2、主动放弃事务3、全部回滚:4、部分支持事务:5、WATCH: 三、事务三阶段四、小结 redis是支持事务的&#xff0c;但是它与传统的关系型数据库中的事务是有所不同的 一、概述 概念: 可以一次执行多个命令&#xff0c;本质是一…

《解决方案架构师修炼之道》读书笔记

1. 水在前面 第一次看到“解决方案架构师”这个词&#xff0c;就把这本书借回家翻了。最近也在网上看点资料&#xff0c;对比起来发现还是出版物内容更完整和更有体系&#xff0c;而且看书真的能让人安静下来。。。 《解决方案架构师修炼之道》所罗伯死里瓦斯塔瓦&#xff0c;内…

每天五分钟深度学习pytorch:pytorch中的广播机制是什么?

本文重点 在pytorch中经常有张量和张量之间的运算,那么有一点需要注意,那就是维度要匹配,如果维度不匹配就有可能出现问题。如果维度不一致,此时也可以同时进行操作,此时就需要使用pytorch中的广播机制,本节课程就讲解pytorch中的广播机制。 广播机制示意图 如上就是py…

RK3568技术笔记之三 SAIL-RK3568开发板板卡功能测试

从这里开始&#xff0c;就是老生常谈系列之一&#xff1a;板卡功能测试。 放一张图镇一下帖 按照我自己顺手的方式&#xff0c;把这板子功能测一下。 先把开发板串口信息打印出来。 工具 功能 备注 电脑&#xff08;必备&#xff09; 提供使用终端软件环境 需要具备至少…

【ArcGIS微课1000例】0117:ArcGIS中如何将kml(kmz)文件转json(geojson)?

文章目录 一、kml获取方式二、kml转图层三、图层转json一、kml获取方式 kml文件是一种很常用的数据格式,可以从谷歌地球(googleearth)获取某一个地区的kml范围文件,如青海湖(做好的kml文件可以从配套实验数据包0117.rar中获取)。 二、kml转图层 打开【KML转图层】工具,…

2 程序的灵魂—算法-2.4 怎样表示一个算法-2.4.2 用流程图表示算法

流程图表示算法&#xff0c;直观形象&#xff0c;易于理解。 【例 2.6】将例 2.1 求 5!的算用流程图表示。 【例 2.7】将例 2.2 的算用流程图表示。 【例 2.8】将例 2.3 判定闰年的算用流程图表示。

问题:功夫菜产品的成本由哪几方面构成() #经验分享#其他

问题&#xff1a;功夫菜产品的成本由哪几方面构成&#xff08;&#xff09; A&#xff0e;材料成本&#xff08;标准投料2%损耗&#xff09;包材成本直接人工费固定加工费 B&#xff0e;&#xff08;材料成本包材成本&#xff09;*&#xff08;1加价率&#xff09; C&#x…

Polar Web 【简单】- 被黑掉的站

Polar Web 【简单】- 被黑掉的站 Contents Polar Web 【简单】- 被黑掉的站思路EXP运行&总结 思路 如题目所述&#xff0c;这是一个被黑掉的站点&#xff0c;由此不禁要了解该黑客发现了哪些可以入手的路径&#xff0c;或是留下了什么样的文件供持续访问。 目录扫描该站点发…

【C++修行之道】类和对象(二)类的6个默认成员函数、构造函数、析构函数

目录 一、类的6个默认成员函数 二、构造函数 2.1 概念 2.2 特性 2.2.5 自动生成默认构造函数 不进行显示定义的隐患&#xff1a; 2.2.6 自动生成的构造函数意义何在&#xff1f; 两个栈实现一个队列 2.2.7 无参的构造函数和全缺省的构造函数都称为默认构造函数&#x…

计算机组成原理(一)

冯诺依曼机器的特征&#xff1a; 指令和数据以同等的地位存储在存储器当中指令和数据都是二进制指令和数据都是保存在存储器当中的 存储字 每个存储单元中的数据&#xff0c;称为存储字 存储字长 存储单元能够存储的二进制数据的长度 在一个8位系统中&#xff0c;字长是…

Java——IO流(一)-(1/8):File、IO流概述、File文件对象的创建(介绍、实例演示)

目录 File IO流概述 File文件对象的创建 介绍 实例演示 File 存储数据的方案 变量 double money 9999.5 数组 int[] age new int[100];对象 Student s new Student()集合 List<Student> students new ArrayList<>()…

[office] Excel教学:Excel通配符怎么用? #其他#职场发展

Excel教学&#xff1a;Excel通配符怎么用&#xff1f; 尽管Excel使用了很多年&#xff0c;但很多人都还是忽略了Excel通配符的存在&#xff0c;不知道通配符是什么&#xff0c;不知道如何使用它。今天我就完整地介绍一下通配符&#xff0c;让你彻底地认识通配符。 关于通配符…

递归(全排列andN皇后)

全排列 分治与递归 递归是实现分治的一种方法 思想思路 题目&#xff1a; 全排列i 我这样直接输出会多输出一个空行&#xff08;最后一个\n&#xff09; #include<stdio.h>using namespace std; const int maxn10; int an[maxn]; int n; bool hash[maxn]{0}; int c0…

微服务架构-可见可观测与量化分析体系

目录 一、可见可观测 1.1 概述 1.2 服务可见性 1.2.1 概述 1.2.2 服务描述 1.2.3 服务所有权 1.2.4 服务对外接口 1.2.5 服务SLA 1.2.6 服务的上下游拓扑 1.2.7 服务变更 1.2.8 服务接入和资源配额管理 1.2.9 服务线上部署和线下测试环境信息 1.3 变更可见性 1.4 …

赚钱而已,你又不是宠物,干嘛让所有人都喜欢你?

* 大家好&#xff0c;我是前端队长。前端程序员&#xff0c;2023年开始玩副业。做过AI绘画&#xff0c;公众号 AI 爆文&#xff0c;AI代写项目&#xff0c;累计变现五位数。 — 今天看到一句话说的真好&#xff1a; 太多人总想让别人喜欢自己了。有什么用&#xff0c;你又不是宠…