• 基于红黑树对map和set容器的封装


    在这里插入图片描述

    本章代码gitee仓库:map和set模拟实现stl_map_set_tree源码

    🐱1. 红黑树的泛型

    我们通过查看源码,发现mapset的底层都是红黑树,用的同一个类模板,通过控制传不同的模板参数,从而实例化出不同的类
    image-20230913160728813

    🐈1.1 红黑树节点

    因为要对mapset进行封装,set是K模型,map是KV模型,所以采用模板来对数据类型进行控制

    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)
    	{}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    🐈1.2 红黑树迭代器

    STL库里面红黑树设置了一个头节点,这样在而我们采用的不是库里面方式,用的自己手搓的红黑树,所以在迭代器++--的时候,采用遍历树的方式
    在这里插入图片描述

    迭代器结构:

    这里有三个模板参数,PtrRef可以通过传过来的是普通参数还是const参数来控制采用什么样的迭代器(普通迭代器或者const迭代器);同时也为mappair的两个参数通过了很好的访问方式Ref operator*()Ptr operator->()

    template<class T,class Ptr,class Ref>
    struct __TreeIterator
    {
    	typedef RBTreeNode<T> Node;
    	typedef __TreeIterator<T, Ptr, Ref> Self;
    	typedef __TreeIterator<T, T*, T&> Iterator;
    	Node* _node;
    	//根据实例化的迭代器选择是构造还是拷贝构造
    	__TreeIterator(const Iterator&it)
    		:_node(it._node)
    	{}
    
    	__TreeIterator(Node* node)
    		:_node(node)
    	{}
    
    	Ref operator*()
    	{
    		return _node->_data;
    	}
    
    	Ptr operator->()
    	{
    		return &_node->_data;
    	}
    
    	bool operator!=(const Self& s) const
    	{
    		return _node != s._node;
    	}
    
    	bool operator==(const Self& s) const
    	{
    		return _node == s._node;
    	}
    
    	Self& operator++()
    	{
    		if (_node->_right)
    		{
    			//右不为空,访问右子树的最左节点(最小节点)
    			Node* subLeft = _node->_right;
    			while (subLeft->_left)
    			{
    				subLeft = subLeft->_left;
    			}
    			_node = subLeft;
    		}
    		else
    		{
    			//右为空
    			Node* cur = _node;
    			Node* parent = cur->_parent;
    			//孩子是父亲左的祖先节点
    			while (parent && cur == parent->_right)
    			{
    				cur = cur->_parent;
    				parent = parent->_parent;
    			}
    			_node = parent;
    		}
    		return *this;
    	}
    
    	Self& operator--()
    	{
    		if (_node->_left)
    		{
    			Node* subRight = _node->_left;
    			while (subRight->_right)
    			{
    				subRight = subRight->_right;
    			}
    			_node = subRight;
    		}
    		else
    		{
    			//孩子是父亲右的节点
    			Node* cur = _node;
    			Node* parent = cur->_parent;
    			while (parent && cur == parent->_left)
    			{
    				cur = cur->_parent;
    				parent = parent->_parent;
    			}
    			_node = parent;
    		}
    		return *this;
    	}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90

    迭代器:

    template<class K,class T,class KeyOfT>
    struct RBTree
    {
    	typedef RBTreeNode<T> Node;
    
    public:
    	typedef __TreeIterator<T, T*, T&> iterator;
    	typedef __TreeIterator<T, const T*, const T&> const_iterator;	//const迭代器
    
    	//迭代器
    	iterator begin()
    	{
    		Node* leftMin = _root;
    		while (leftMin && leftMin->_left)
    		{
    			leftMin = leftMin->_left;
    		}
    		return leftMin;
    	}
    
    	iterator end()
    	{
    		return iterator(nullptr);
    	}
    
    	const_iterator begin() const
    	{
    		Node* leftMin = _root;
    		while (leftMin && leftMin->_left)
    		{
    			leftMin = leftMin->_left;
    		}
    		return leftMin;
    	}
    
    	const_iterator end() const
    	{
    		return const_iterator(nullptr);
    	}
        
        // ... ...
        // ... ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    🐈1.3 仿函数

    当对元素进行插入或者查找的时候,都要进行比较,而mappair无法直接比较,所以我们设置了仿函数,用来提取key
    这里以Find接口为例,具体可以看代码仓库的完整代码

    Node* Find(const K& key)
    {
    	Node* cur = _root;
    	//仿函数
    	KeyOfT kot;
    	while (cur)
    	{
    		//提出key值
    		if (kot(cur->_data) > key)
    		{
    			cur = cur->_left;
    		}
    		else if (kot(cur->_data) < key)
    		{
    			cur = cur->_right;
    		}
    		else
    			return cur;
    	}
    	return nullptr;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    🐯2. 对set的封装

    对于set的封装相对简单,在整个设计中,set由于只有一个K值,所以很多模板参数对于set而已作用并不大

    image-20230914174644288

    set的K值是不允许修改的,所以不管是普通迭代器还是const迭代器,都是采用的const迭代器

    这里关于插入的操作,本质上也是为了兼容map,放到下面和map一起说

    namespace My_map_set
    {
    	template<class K>
    	class set
    	{
            //仿函数
    		struct SetKeyOfT
    		{
    			const K& operator()(const K& key)
    			{
    				return key;
    			}
    		};
    	public:
    		typedef typename RBTree<K, K, SetKeyOfT>::const_iterator iterator;
    		typedef typename RBTree<K, K, SetKeyOfT>::const_iterator const_iterator;
    
    		const_iterator begin() const
    		{
    			return _t.begin();
    		}
    
    		const_iterator end() const
    		{
    			return _t.end();
    		}
    
    		pair<iterator, bool> insert(const K& key)
    		{
    			pair < typename RBTree<K, K, SetKeyOfT>::iterator, bool > ret = _t.Insert(key);
    			return pair<iterator, bool>(ret.first, ret.second);
    		}
    	private:
    		RBTree<K, K, SetKeyOfT> _t;
    	};
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    我们在封装的时候,采用了一个仿函数SetKeyOfT,这是因为在数据比较的时候,mappair需要取出里面的key值,为了保持统一,我们对set也使用了仿函数

    🦄3. 对map的封装

    • mapkey值是不允许修改的,而value是允许修改的,使用有const修饰pair里面的key

      image-20230914185106723

    • map是支持[]的,而[]底层又是调用的insert,所以对于insert的返回值需要进行更改,而map有普通迭代器和const迭代器,不需要指定调用树里面的

      set的迭代器都是const迭代器,所以我们直接调用树里面的迭代器,库里面就是这么实现的:

      image-20230914193944187

      当这个类被实例化成const迭代器的时候,是一个构造函数,支持了普通迭代器构造const迭代器;

      当这个类被实例化为普通迭代器,那就是拷贝构造

      image-20230914195622767

    namespace My_map_set
    {
    	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;
    		typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::const_iterator const_iterator;
    
    		iterator begin()
    		{
    			return _t.begin();
    		}
    
    		iterator end()
    		{
    			return _t.end();
    		}
    
    		const_iterator begin() const
    		{
    			return _t.begin();
    		}
    
    		const_iterator end() const
    		{
    			return _t.end();
    		}
    
    		V& operator[](const K& key)
    		{
    			pair<iterator, bool> ret = insert(make_pair(key, V()));
    			return ret.first->second;
    		}
    
    		pair<iterator, bool> insert(const pair<K, V>& kv)
    		{
    			return _t.Insert(kv);
    		}
    
    	private:
    		RBTree<K, pair<const K, V>, MapKeyOfT> _t;
    	};
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    这里的封装相比之前的vectorlist这些容器模拟实现,会麻烦一点,本章也只是对于mapset的核心功能进行实现,具体的可以参考源码进行学习,

    那本期的分享就到这里咯,我们下期再见,如果还有下期的话。

  • 相关阅读:
    构建“零工市场小程序”,服务灵活就业“大民生”
    ZYNQ多通道数据采集与LWIP传输系统
    [2022 牛客多校2 E] Falfa with Substring (二项式反演 NTT)
    MySQL Insert 后獲得主鍵
    python如何将程序编译成exe
    CPU监控工具(CPU使用率及CPU温度监控)
    5.jeecg的登录及权限(jwt+shiro)
    413报错(nginx :请求体大小超出最大限制)
    sonarlint report监测结果分类
    留意差距:弥合网络安全基础设施的挑战
  • 原文地址:https://blog.csdn.net/Dirty_artist/article/details/132889940