C++:set和map的底层封装模拟实现

目录

底层对比:

底层红黑树结构和set、map: 

底层模拟:

传值调用:

迭代器:

operator ++()

 find函数

operator() 、仿函数 

set和map的仿函数 :

图解: 

insert函数:

构造函数,析构函数: 

 析构函数:

拷贝构造函数: 

赋值函数: 

map的封装:

set的封装: 

红黑树的修改: 



底层对比:

上图以kv模型为例 

通过底层可以看出:

  • set和map的底层调用都是调用了红黑树 rb_tree
  • set和map在底层最大的区别就是value的不同,set的value仍然是key类型的,但是map的value是pair<const key,T>类型的
  • 也因为类型的不同,二者使用的template 类模板也相差了一个class T ,而这个class T表示的其实就是map的value中的second的数值类型
  • 同时因为value的不同,所以导致了map和set在红黑树中存储的数据也并不相同

底层红黑树结构和set、map: 

底层模拟:

传值调用:

迭代器:

  • 不论是set还是map 的迭代器,关键得是看红黑树的迭代器
  • 树的内部迭代器其实就是和链表相差不多,需要进行一个内部的重载调用
  • 也就是说 operator-> operator * operator ++ operator --都是需要进行内部重载,然后进行封装的!
 RBTree.h

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->()
	{
		return &_node->_data;
	}

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

	Self& operator++()
	{
		if (_node->_right)
		{
			// 下一个,右树最左节点
			Node* leftMin = _node->_right;
			while (leftMin->_left)
			{
				leftMin = leftMin->_left;
			}

			_node = leftMin;
		}
		else
		{
			// 下一个,孩子等于父亲左的那个祖先
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_right)
			{
				cur = parent;
				parent = parent->_parent;
			}

			_node = parent;
		}

		return *this;
	}
};

operator ++()

是实现迭代器的难点之一,因为红黑树的本质其实是二叉搜索树,所以遍历是使用中序遍历,所以++和--都是使用 中序遍历的节点,如下图所示,it所在位置的下一个节点是15 

 首先我们需要知道,中序遍历的过程是 左子树 根节点 右子树 所以可以通过下图右可以得出:

  • 当前it所在的位置是它父亲节点的左节点,那么下一个需要遍历的节点就是it的父亲节点
  • 当前节点的右子树是空的,那么表示这个节点的右子树结束遍历,下一个需要遍历和++递达的节点,应该往上进行查找祖先节点
  • 如果当前节点的右子树是空的,且当前节点是其父亲节点的右节点,那么表示要去父亲节点更往上的方向寻找下一个需要遍历的节点
  • 如果当前节点的右子树是空的/遍历完了,且当前节点是其父亲节点的左节点,那么表示当前节点结束,返回父亲节点且下一个访问的节点就是父亲节点,且同时访问完后需要取父亲节点的右子树中进行遍历和寻找下一个++位置的节点
  • 最后一个单独的处理,那就是走到最后一个节点的时候,走到最后一个节点的右子树是空的,就一直返回到根节点,这时候就需要给迭代器置空了!

 

  • ++的下一个节点是该节点右子树中的最左节点!
  • leftMin = node->right 表示进入右子树中寻找最左节点,当然现在的条件是右子树存在,如果右子树存在就是上面,如果不存在就需要往上边寻找,直到找到某个节点是它父亲节点的左节点为止,或者说找到的某个节点,他没有父亲节点它是根节点为止!

  • 要求当前的节点是他父亲节点的右节点,如果一直是这样就需要一直往上走,直到当前节点是它父亲节点的左节点时,返回父亲节点
  • 且需要注意如果当前节点是根节点,那么它的父亲就是空的,空的不能再继续往上寻找了,所以直接跳出循环,最后返回父亲节点!
RBTree.h
//红黑树中调动迭代器的函数方法
typedef __RBTreeIterator<T, T&, T*> Iterator;
typedef __RBTreeIterator<T, const T&, const T*> ConstIterator;

Iterator Begin()
	{
		Node* leftMin = _root;
		while (leftMin && leftMin->_left)
		{
			leftMin = leftMin->_left;
		}

		return Iterator(leftMin);
	}

	Iterator End()
	{
		return Iterator(nullptr);
	}

	ConstIterator End() const
	{
		return ConstIterator(nullptr);
	}

	ConstIterator Begin() const
	{
		Node* leftMin = _root;
		while (leftMin && leftMin->_left)
		{
			leftMin = leftMin->_left;
		}

		return ConstIterator(leftMin);
	}

 find函数

RBTree.h

Iterator Find(const K& key)
	{
		Nod
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值