当前位置: 首页 > news >正文

红黑树进阶封装实现C++_map_set(源码剖析)

怎么会这样???学会了红黑树却学不会map和set,别怕别怕,有小博在,这还不是手到擒来吗!之前小博已经带着大家熟悉了红黑树的底层结构,那么今天不管你是小白中的小白,还是大神中的大神,都欢迎和小博一起讨论我们今天的话题——map和set的封装!!!

一.C++_STL源码剖析

首先我们先来看一下C++标准库中实现map和set容器结构的核心框架:

// set
#ifndef __SGI_STL_INTERNAL_TREE_H
#include <stl_tree.h>
#endif
#include <stl_set.h>
#include <stl_multiset.h>// map
#ifndef __SGI_STL_INTERNAL_TREE_H
#include <stl_tree.h>
#endif
#include <stl_map.h>
#include <stl_multimap.h>// stl_set.h
template <class Key, class Compare = less<Key>, class Alloc = alloc>
class set {
public:
// typedefs:typedef Key key_type;typedef Key value_type;
private:typedef rb_tree<key_type, value_type,identity<value_type>, key_compare, Alloc> rep_type;rep_type t; // red-black tree representing set
};// stl_map.h
template <class Key, class T, class Compare = less<Key>, class Alloc = alloc>
class map {
public:// typedefs:typedef Key key_type;typedef T mapped_type;typedef pair<const Key, T> value_type;
private:typedef rb_tree<key_type, value_type,select1st<value_type>, key_compare, Alloc> rep_type;rep_type t; // red-black tree representing map
};// stl_tree.h
struct __rb_tree_node_base
{typedef __rb_tree_color_type color_type;typedef __rb_tree_node_base* base_ptr;color_type color;base_ptr parent;base_ptr left;base_ptr right;
};// stl_tree.h
template <class Key, class Value, class KeyOfValue, class Compare, class Alloc
= alloc>
class rb_tree {
protected:typedef void* void_pointer;typedef __rb_tree_node_base* base_ptr;typedef __rb_tree_node<Value> rb_tree_node;typedef rb_tree_node* link_type;typedef Key key_type;typedef Value value_type;
public:// insert⽤的是第⼆个模板参数左形参pair<iterator,bool> insert_unique(const value_type& x);// erase和find⽤第⼀个模板参数做形参size_type erase(const key_type& x);iterator find(const key_type& x);
protected:size_type node_count; // keeps track of size of treelink_type header;
};template <class Value>
struct __rb_tree_node : public __rb_tree_node_base
{typedef __rb_tree_node<Value>* link_type;Value value_field;
};

①通过下图对框架的分析,我们可以看到源码中rb_tree⽤了⼀个巧妙的泛型思想实现,rb_tree是实
现key的搜索场景还是key/value的搜索场景不是固定的的,⽽是由第⼆个模板参数Value决定
_rb_tree_node中存储的数据类型。

② set实例化rb_tree时第⼆个模板参数给的是key,map实例化rb_tree时第⼆个模板参数给的是
pair<const key, T>,这样⼀颗红⿊树既可以实现key搜索场景的set,也可以实现key/value搜索场
景的map。

③ 注意,源码⾥⾯模板参数是⽤T代表value,⽽内部写的value_type不是我们我们之前key/value场景中说的value,源码中的value_type反而是红⿊树结点中存储的真实的数据类型。

问题: rb_tree中第⼆个模板参数Value已经控制了红⿊树结点中存储的数据类型,那为什么还要传第⼀个模板参数Key呢?尤其是set,两个模板参数是⼀样的。

解答:要注意的是对于map和set,find/erase时的函数参数都是Key,所以第⼀个模板参数是传给find/erase等函数做形参的类型的。对于set⽽⾔两个参数是⼀样的,但是对于map而言就完全不⼀样了,map在insert的是pair对象,但是find和ease的是Key对象。

在这里插入图片描述

二.红黑树模拟实现map和set

1. 实现出红⿊树的框架,并初步⽀持insert操作

因为红黑树实现了泛型结构,所以我们不知道T参数是K,还是pair<K, V>,那么insert内部进⾏插⼊逻辑⽐较时,就没办法进⾏⽐较,因为pair默认⽀持的是key和value⼀起参与⽐较,而我们需要的是任何时候只⽐较key,所以我们在map和set层分别实现⼀个MapKeyOfT和SetKeyOfT的仿函数传给
红黑树的KeyOfT,然后红黑树中通过KeyOfT仿函数取出T类型对象中的key,再进⾏⽐较,具体细节参考如下代码实现。

// 源码中pair⽀持的<重载实现
template <class T1, class T2>
bool operator< (const pair<T1,T2>& lhs, const pair<T1,T2>& rhs)
{ return lhs.first<rhs.first || (!(rhs.first<lhs.first) &&lhs.second<rhs.second); 
}//myset.h
namespace ph
{template<class K>class set{public:struct SetKeyOfT{const K& operator()(const K& key){return key;}};bool Insert(const K& key){return  _t.Insert(key);}private:RBTree<K,const K, SetKeyOfT> _t;};
}//mymap.h
namespace ph
{template<class K,class V>class map{public:struct MapKeyOfT{const K& operator()(const pair<K, V>& kv){return kv.first;}};bool Insert(const pair<K, V>& kv){return _t.Insert(kv);}private:RBTree<K, pair<const K,V>, MapKeyOfT> _t;};
}enum Colour
{RED,BLACK
};template<class T>
struct RBTreeNode
{T _data;RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;Colour _col;RBTreeNode(const T& data): _data(data), _left(nullptr), _right(nullptr), _parent(nullptr){}
};template<class K, class T, class KeyOfT>
class RBTree
{
private:typedef RBTreeNode<T> Node;Node* _root = nullptr;
public:bool Insert(const T& data){if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;return 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 false;}}cur = new Node(data);Node* newnode = cur;cur->_col = RED;if (kot(parent->_data) < kot(data)){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;//...return true;}
};

2. 迭代器iterator的实现

OK,我们还是先来看一下标准库中迭代器的实现思路:

struct __rb_tree_base_iterator
{typedef __rb_tree_node_base::base_ptr base_ptr;base_ptr node;void increment(){if (node->right != 0) {node = node->right;while (node->left != 0)node = node->left;}else {base_ptr y = node->parent;while (node == y->right) {node = y;y = y->parent;}if (node->right != y)node = y;}}void decrement(){if (node->color == __rb_tree_red && node->parent->parent == node)node = node->right;else if (node->left != 0) {base_ptr y = node->left;while (y->right != 0)y = y->right;node = y;}else {base_ptr y = node->parent;while (node == y->left) {node = y;y = y->parent;}node = y;}}
};template <class Value, class Ref, class Ptr>
struct __rb_tree_iterator : public __rb_tree_base_iterator
{typedef Value value_type;typedef Ref reference;typedef Ptr pointer;typedef __rb_tree_iterator<Value, Value&, Value*> iterator;__rb_tree_iterator() {}__rb_tree_iterator(link_type x) { node = x; }__rb_tree_iterator(const iterator& it) { node = it.node; }reference operator*() const { return link_type(node)->value_field; }#ifndef __SGI_STL_NO_ARROW_OPERATORpointer operator->() const { return &(operator*()); }#endif /* __SGI_STL_NO_ARROW_OPERATOR */self& operator++() { increment(); return *this; }self& operator--() { decrement(); return *this; }inline bool operator==(const __rb_tree_base_iterator& x,const __rb_tree_base_iterator& y) {return x.node == y.node;}inline bool operator!=(const __rb_tree_base_iterator& x,const __rb_tree_base_iterator& y) {return x.node != y.node;
};

iterator实现思路分析

① iterator实现的⼤框架跟list的iterator思路是⼀致的,⽤⼀个类型封装结点的指针,再通过重载运算
符实现迭代器像指针⼀样访问的⾏为。

② map和set的迭代器⾛的是中序遍历,左⼦树->根结点->右⼦树,那么begin()会返回中序第⼀个结点的iterator也就是下图10所在结点的迭代器。

在这里插入图片描述

③ 迭代器++的核⼼逻辑只考虑当前中序局部遍历要访问的下⼀个结点。

④ 迭代器++时,如果it指向的结点的右⼦树不为空,代表当前结点已经访问完了,则要访问的下⼀个结点是右⼦树的中序第⼀个节点,⼀棵树中序遍历的第⼀个节点是最左结点,所以直接查找右⼦树的最左结点即可。

⑤ 迭代器++时,如果it指向的结点的右⼦树为空,代表当前结点已经访问完了且当前结点所在的⼦树也访问完了,则要访问的下⼀个结点在当前结点的祖先⾥⾯,所以要沿着当前结点到根节点的路径向上找。

⑥ 如果当前结点是⽗亲的左孩子,那么下⼀个访问的结点就是当前结点的⽗亲。

⑦ 如果当前结点是⽗亲的右孩子,当前当前结点所在的⼦树访问完了,当前结点所在⽗亲的⼦树也访问完了,那么下⼀个要访问的节点需要继续往根的祖先中去查找,直到找到孩⼦是⽗亲的左孩子的那个祖先就是中序遍历要访问的下⼀个结点。

⑧ 那么迭代器的end()如何表示呢?如下图:当it指向50时,++it时,50是40的右孩子,40是30的右孩子,30是18的右孩子,18是根节点,没有⽗亲,所以没有找到孩⼦是⽗亲左孩子的那个祖先,这时⽗亲为空了,那我们就把it中的结点指针置为nullptr,我们⽤nullptr去充当end()。需要注意的是stl源码中,红⿊树增加了⼀个哨兵位头结点作为end(),这个哨兵位头结点和根互为⽗亲,左指针指向最左结点,右指针指向最右结点。相⽐我们⽤nullptr作为end(),差别不⼤。

在这里插入图片描述

在这里插入图片描述

⑨ set的iterator也不⽀持修改,我们把set的第⼆个模板参数改成const K即可。

⑩ map的iterator不⽀持修改key但是可以修改value,我们把map的第⼆个模板参数pair的第⼀个参
数改成const K即可。

3. map支持operator[]

map要⽀持[]主要需要修改insert返回值来⽀持。

4. 代码示例

4.1

//RBTree.h#pragma once
#include <iostream>//1.封装map_set
//2.迭代器
//3.key修改
//4.operator[]enum Color
{BLACK,RED,
};template<class T>
struct RBTreeNode
{T _data;RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;Color _col;RBTreeNode(const T& data):_data(data),_left(nullptr),_right(nullptr),_parent(nullptr){}
};template<class T,class Ref,class Ptr>
struct RBTreeIterator
{typedef RBTreeNode<T> TreeNode;typedef RBTreeIterator<T,Ref,Ptr> Self;TreeNode* _node;TreeNode* _root;RBTreeIterator(TreeNode* node,TreeNode* root):_node(node),_root(root){ }Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}Self& operator++(){//左根右if (_node->_right){TreeNode* cur = _node->_right;while (cur && cur->_left){cur = cur->_left;}_node = cur;}else{TreeNode* cur = _node;TreeNode* parent = _node->_parent;while (parent && cur==parent->_right){cur = parent;parent = parent->_parent;}_node = parent;}return *this;}Self operator++(int){Self Iteratmp(*this);if (_node->_right){TreeNode* cur = _node->_right;while (cur && cur->_left){cur = cur->_left;}_node = cur;}else{TreeNode* cur = _node;TreeNode* parent = _node->_parent;while (parent && cur == parent->_right){cur = parent;parent = parent->_parent;}_node = parent;}return Iteratmp;}Self& operator--(){//右根左if (_node == nullptr){TreeNode* cur = _root;while (cur && cur->_right){cur = cur->_right;}_node = cur;}else if (_node->_left){TreeNode* cur = _node->_left;while (cur && cur->_right){cur = cur->_right;}_node = cur;}else{TreeNode* cur = _node;TreeNode* parent = _node->_parent;while (parent && cur == parent->_left){cur = parent;parent = parent->_parent;}_node = parent;}return *this;}Self operator--(int){Self Iteratmp(*this);if (_node == nullptr){TreeNode* cur = _root;while (cur && cur->_right){cur = cur->_right;}_node = cur;}else if (_node->_left){TreeNode* cur = _node->_left;while (cur && cur->_right){cur = cur->_right;}_node = cur;}else{TreeNode* cur = _node;TreeNode* parent = _node->_parent;while (parent && cur == parent->_left){cur = parent;parent = parent->_parent;}_node = parent;}return Iteratmp;}bool operator==(const Self& s){return _node == s._node;}bool operator!=(const Self& s){return _node != s._node;}
};template<class K,class T,class KeyOfT>
class RBTree
{
public:typedef RBTreeNode<T> TreeNode;typedef RBTreeIterator<T, T&, T*> Iterator;typedef RBTreeIterator<T, const T&, const T*> Const_Iterator;Iterator Begin(){TreeNode* cur = _root;while (cur && cur->_left){cur = cur->_left;}return Iterator(cur, _root);}Const_Iterator Begin() const{TreeNode* cur = _root;while (cur && cur->_left){cur = cur->_left;}return Iterator(cur, _root);}Iterator End(){return Iterator(nullptr, _root);}Const_Iterator End() const{return Iterator(nullptr, _root);}Iterator Find(const K& key){TreeNode* pcur = _root;KeyOfT kot;while (pcur){if (kot(pcur->_data) >key){pcur = pcur->_left;}else if (kot(pcur->_data) < key){pcur = pcur->_right;}else{return Iterator(pcur, _root);}}return Iterator(nullptr, _root);}pair<Iterator,bool> Insert(const T& data){if (_root == nullptr){_root = new TreeNode(data);_root->_col = BLACK;return make_pair(Iterator(_root,_root),true);//return {Iterator(_root,_root),true};}KeyOfT kot;TreeNode* pcur = _root;TreeNode* parent = nullptr;while(pcur){if (kot(pcur->_data) > kot(data)){parent = pcur;pcur = pcur->_left;}else if (kot(pcur->_data) < kot(data)){parent = pcur;pcur = pcur->_right;}else{return {Iterator(pcur,_root),false};}}pcur = new TreeNode(data);TreeNode* newnode = pcur;pcur->_col = RED;if (kot(pcur->_data) > kot(data)){parent->_left = pcur;}else{parent->_right = pcur;}pcur->_parent = parent;while (parent && parent->_col == RED){TreeNode* grandfather = parent->_parent;//p为g的左if (parent == grandfather->_left){TreeNode* uncle = grandfather->_right;if (uncle && uncle->_col == RED){//uncle存在且为红-->变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;pcur = grandfather;parent = pcur->_parent;}else{//uncle不存在/uncle存在且为黑if (pcur == parent->_left){//     g//   p//pc//右旋+变色RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else  { //    g// p//   pc//左右双旋+变色RotateL(parent);RotateR(grandfather);pcur->_col = BLACK;grandfather->_col = RED;}break;}}else  //p为g的右{TreeNode* uncle = grandfather->_left;if (uncle && uncle->_col == RED){//uncle存在且为红-->变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;pcur = grandfather;parent = pcur->_parent;}else{//uncle不存在/uncle存在且为黑if (pcur == parent->_left){//  g//    p//  pc//右左双旋+变色RotateR(parent);RotateL(grandfather);pcur->_col = BLACK;grandfather->_col = RED;}else{//  g//    p//     pc//左单旋+变色RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return {Iterator(newnode,_root),true};}void RotateR(TreeNode* parent){TreeNode* subL = parent->_left;TreeNode* subLR = subL->_right;subL->_right = parent;parent->_left = subLR;if (subLR){subLR->_parent = parent;}TreeNode* parentParent = parent->_parent;parent->_parent = subL;if (_root == parent){_root = subL;}else{if (parentParent->_left == parent){parentParent->_left = subL;}else{parentParent->_right = subL;}}subL->_parent = parentParent;}void RotateL(TreeNode* parent){TreeNode* subR = parent->_right;TreeNode* subRL = subR->_left;subR->_left = parent;parent->_right = subRL;if (subRL){subRL->_parent = parent;}TreeNode* parentParent = parent->_parent;parent->_parent = subR;if (_root == parent){_root = subR;}else{if (parentParent->_left == parent){parentParent->_left = subR;}else{parentParent->_right = subR;}}subR->_parent = parentParent;}private:TreeNode* _root = nullptr;
};

4.2

#pragma once#include "RBTree.h"namespace ph
{template<class K>class set{public:struct SetKeyOfT{const K& operator()(const K& key){return key;}};typedef typename RBTree<K,const K, SetKeyOfT>::Iterator iterator;typedef typename RBTree<K,const K,SetKeyOfT>::Const_Iterator const_iterator;iterator begin(){return _t.Begin();}const_iterator begin() const{return _t.Begin();}iterator end(){return _t.End();}const_iterator end() const{return _t.End();}pair<iterator, bool> Insert(const K& key){return  _t.Insert(key);}iterator Find(const K& key){return _t.Find(key);}private:RBTree<K,const K, SetKeyOfT> _t;};
}

4.3

#pragma once#include "RBTree.h"namespace ph
{template<class K,class V>class map{public:struct MapKeyOfT{const K& operator()(const pair<K, V>& kv){return kv.first;}};typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::Iterator iterator;typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::Const_Iterator const_iterator;iterator begin(){return _t.Begin();}const_iterator begin() const{return _t.Begin();}iterator end(){return _t.End();}const_iterator end() const{return _t.End();}pair<iterator,bool> Insert(const pair<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;}iterator Find(const pair<const K, V>& kv){return _t.Find(kv.first);}private:RBTree<K, pair<const K,V>, MapKeyOfT> _t;};
}

好了,欢乐的时光总是过得很快,看到这里,相信不少锲而不舍的铁子们都已经收获满满了,那我们就下期再见喽!!!

http://www.dtcms.com/a/614428.html

相关文章:

  • 服装设计手稿设计图外贸网站优化谷歌关键词排名
  • AJAX JSON学习
  • 网站商品管理功能中国建设工程信息网官网入口
  • python 图片解密:base64
  • springmvc请求参数
  • 第四章 防火墙设备管理
  • LLM之Agent(三十)|使用 LangGraph 构建可用于生产环境的智能聊天机器人:完整工程指南
  • 网站收录作用wordpress表格线
  • 成都网站设计师福鼎手机网站建设
  • ESP32模组选型速览
  • 模板网站的缺点重庆专业网站推广流程
  • 什么网站可以查建设用地规划许可证二维码图片制作
  • [Mac] Unclutter 2.2.15 Mac上的文件暂存 智能剪贴板历史记录管理器工具
  • 东莞企业网站seo如何申请域名网站注册
  • 微信网页制作网站建设一建建设网站
  • 网络营销推广方式有哪些seo建站
  • 【算法】逻辑回归
  • Android开发-java版:Framgent
  • 网站设计行业背景稳稳在哪个网站做的消防直播
  • 四川建设银行手机银行下载官方网站下载苏州微网站建设
  • 【HarmonyOS】ArkTS的多线程并发(下)——线程间通信对象的传递
  • 平价网站平价网站建设建设钓鱼网站查询系统
  • 毕业做网站运营好吗亚马逊网站开发的技术
  • 电子商务网站建设与管理课后第四章广州市安全教育平台登录入口
  • LeetCode算法日记 - Day 103: 不同的子序列
  • 怎么建立自己网站 asp个人怎么做网站排名优化
  • 20251109 树状DP总结
  • C语言编译器出现Bug | 如何解决C语言编译器常见问题
  • 做淘宝客网站php青岛网站推广企业
  • 第九篇 扫雷游戏 上(末版·精简)