【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.rhkb.cn/news/344358.html

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

相关文章

01Linux的安装,时区,固定IP的配置

Linux系统的简介与安装 Linux简介 计算机是由硬件和软件所组成 硬件&#xff1a;计算机系统中由电子,机械和光电元件等组成的各种物理装置的总称软件&#xff1a;是用户和计算机硬件之间的接口和桥梁&#xff0c;用户通过软件与计算机进行交流(操作系统) 操作系统作为用户和…

C#WPF数字大屏项目实战03--数据内容区域

1、内容区域划分 第一行标题&#xff0c;放了几个文本框 第二行数据&#xff0c;划分成3列布局 2、第1列布局使用UniformGrid控件 最外面放UniformGrid&#xff0c;然后里面放3个GroupBox控件&#xff0c;这3个groupbox都是垂直排列 3、GroupBox控件模板 页面上的3个Group…

如何使用共享GPU平台搭建LLAMA3环境(LLaMA-Factory)

0. 简介 最近受到优刻得的使用邀请&#xff0c;正好解决了我在大模型和自动驾驶行业对GPU的使用需求。UCloud云计算旗下的[Compshare](https://www.compshare.cn/? ytagGPU_lovelyyoshino_Lcsdn_csdn_display)的GPU算力云平台。他们提供高性价比的4090 GPU&#xff0c;按时收…

零基础入门学用Arduino 第一部分(三)

重要的内容写在前面&#xff1a; 该系列是以up主太极创客的零基础入门学用Arduino教程为基础制作的学习笔记。个人把这个教程学完之后&#xff0c;整体感觉是很好的&#xff0c;如果有条件的可以先学习一些相关课程&#xff0c;学起来会更加轻松&#xff0c;相关课程有数字电路…

el-table合计行前置在首行,自定义合计行方法

背景 el-table原生合计行是在标签内增加show-summary属性&#xff0c;在表尾实现设计合计&#xff0c;且只对表格当前页面显示的列数据进行合计。element-UI效果如下图所示。 现要求在首行显示合计行&#xff0c;并自定义合计逻辑实现如下效果。 图示表格中&#xff0c;成本…

【NI国产替代】产线测试:数字万用表(DMM),功率分析仪,支持定制

数字万用表&#xff08;DMM&#xff09; • 6 位数字表显示 • 24 位分辨率 • 5S/s-250KS/s 采样率 • 电源和数字 I/O 均采用隔离抗噪技术 • 电压、电流、电阻、电感、电容的高精度测量 • 二极管/三极管测试 功率分析仪 0.8V-14V 的可调输出电压&#xff0c;最大连…

【乐吾乐3D可视化组态编辑器】用开关控制巡检车和路灯

一、运动设备开关控制 3D组态编辑器地址&#xff1a;3D可视化组态 - 乐吾乐Le5le 1.在场景中新建模拟运动设备及控制面板&#xff1a;启动/停止 2.单击巡检车设备新建模拟动画 3.设置模拟动画属性 4.单击启动面板&#xff0c;新建交互事件 5.设置交互触发类型&#xff0c;新建…

halcon算子之prepare_object_model_3d详解

为某一操作准备三维对象模型。 Description 操作符prepare_object_model_3d准备3D对象模型ObjectModel3D,用于下面目的中给出的操作。它计算操作所需的值并将其存储在ObjectModel3D中,从而加快了后续操作。没有必要调用prepare_object_model_3d。但是,如果要多次使用3D对象…

YOLOv8改进 | 卷积模块 | 在主干网络中添加/替换蛇形卷积Dynamic Snake Convolution

&#x1f4a1;&#x1f4a1;&#x1f4a1;本专栏所有程序均经过测试&#xff0c;可成功执行&#x1f4a1;&#x1f4a1;&#x1f4a1; 蛇形动态卷积是一种新型的卷积操作&#xff0c;旨在提高对细长和弯曲的管状结构的特征提取能力。它通过自适应地调整卷积核的权重&#xff0…

ARM功耗管理之功耗状态及功耗模式

安全之安全(security)博客目录导读 目录 一、功耗状态定义 ​编辑二、功耗模式定义 三、功耗状态和功耗模式区别 四、功耗模式细分 五、功耗状态细分 1、Core功耗状态 2、Cluster功耗状态 3、设备功耗状态 4、SoC功耗状态 5、功耗状态举例 思考:功耗状态?功耗模式…

链表的中间结点

一、题目链接 https://leetcode.cn/problems/middle-of-the-linked-list/submissions/538121725、 二、思路 定义快慢指针&#xff0c;快指针一次走两步&#xff0c;慢指针一次走一步&#xff0c;最后慢指针的位置就是中间结点的位置 三、题解代码 //快慢指针&#xff0c;快…

容器运行nslookup提示bash: nslookup: command not found【笔记】

在容器中提示bash: nslookup: command not found&#xff0c;表示容器中没有安装nslookup命令。 可以通过以下命令安装nslookup&#xff1a; 对于基于Debian/Ubuntu的容器&#xff0c;使用以下命令&#xff1a; apt-get update apt-get install -y dnsutils对于基于CentOS/R…

用PlantUML描绘C++世界:通过文本描述精准控制UML图的生成

往期本博主的 C 精讲优质博文可通过这篇导航进行查找&#xff1a; Lemo 的C精华博文导航&#xff1a;进阶、精讲、设计模式文章全收录 前言 在编写程序时&#xff0c;可视化的工具可以极大地帮助我们理解和设计复杂的系统。对于C程序员来说&#xff0c;一个强大的工具是UML&am…

【漏洞复现】多客圈子论坛系统 httpGet 任意文件读取漏洞

0x01 产品简介 多客圈子论坛系统是一种面向特定人群或特定话题的社交网络&#xff0c;它提供了用户之间交流、分享、讨论的平台。在这个系统中&#xff0c;用户可以创建、加入不同的圈子&#xff0c;圈子可以是基于兴趣、地域、职业等不同主题的。用户可以在圈子中发帖、评论、…

自定义类型:结构体+结构体内存对齐+结构体实现位段

结构体内存对齐实现位段 一.结构体1.结构体的声明2.结构体变量成员访问操作符3.结构体传参4.匿名结构体5.结构的自引用 二.结构体内存对齐1.对齐规则2.为什么存在内存对齐&#xff1f;3.修改默认对齐数 三.结构体实现位段1.什么是位段2.位段的内存分配3.位段的跨平台问题4.位段…

【c语言】自定义类型----结构体

结构体是c语言的一种自定义类型&#xff0c;自定义类型对于开发者及其重要的类型&#xff0c;它可以随意由开发者进行谱写功能&#xff0c;而今天的结构体可以用来表示一种变量的单个或多种具体属性&#xff0c;再编写代码时有着不可替代的作用&#xff01;&#xff01;&#x…

分享一个按钮代码,主要有html,svg及css动画实现

按钮展示: Switch by Galahhad made with CSS | Uiverse.io 源代码: css .theme-switch {--toggle-size: 30px;/* the size is adjusted using font-size,this is not transform scale,so you can choose any size */--container-width: 5.625em;--container-height: 2.5em;-…

【Vue】面经基础版-案例效果分析

面经效果演示 功能分析 通过演示效果发现&#xff0c;主要的功能页面有两个&#xff0c;一个是列表页&#xff0c;一个是详情页&#xff0c;并且在列表页点击时可以跳转到详情页底部导航可以来回切换&#xff0c;并且切换时&#xff0c;只有上面的主题内容在动态渲染 实现思路…

连山露【诗词】

连山露 雾隐黄山路&#xff0c;十步一松树。 树上惊松鼠&#xff0c;松子衔木屋。 松子青嫩芽&#xff0c;尖尖头探出。 卷挂白露珠&#xff0c;装映黄山雾。

HTML静态网页成品作业(HTML+CSS)—— 节日端午节介绍网页(5个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;未使用Javacsript代码&#xff0c;共有5个页面。 二、作品演示 三、代…