map和set的模拟实现

在实现了红黑树后,我们就可以着手实现一个简易版的map和set了,在粗略地看了一下源码后,发现,map和set底层是使用同一份红黑树的代码,set是K结构,只需要一个模板参数,map是K-V结构需要两个模板参数,如何做到共用一份红黑树代码创建的红黑树来存储数据呢?
源码中的红黑树模板参数如下

template<class K,class T,class KofT>

红黑树的节点结构

enum color
{
	Red,
	Black
};
template<class T>
struct RBTreeNode
{
	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right;
	RBTreeNode<T>* _parent;
	T _data;
	color _co;
	RBTreeNode(const T&val)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		, _data(val)
		,_co(Red)
	{}
};

只用T这个参数来存储数据,不管是set的单个数据,还是map键值对类型的参数,然后用KofT来接受一个仿函数类型,map传的仿函数用于把T接受的键值对类型的第一个数据key提取出来,set传的仿函数就直接返回T接受的数据类型。

template<class K>
	 class set
	 {
	 public:
		 struct setKofT
		 {
			 const K& operator()(const K& val)  //这里就是set的仿函数
			 {
				 return val;
			 }
		 };
		  private:
		 RBTree<K, K, setKofT>_t;   //红黑树需要三个模板参数,set用同一份红黑树代码,K既是存储数据也是用来比较大小建树的参数
	 };
template<class K, class V>
	class map
	{	
	public:
		struct mapKofT
		{
			const K& operator()(const pair<K,V>& val) //map的仿函数用来把pair中的first提出来用在插入节点时的位置寻找。
			{
				return val.first;
			}
		};
		private:
		RBTree<K, pair<K,V>, mapKofT>_t;//map和set虽然用同一份红黑树的代码,但控制传入参数类型不同就可以实现存储K和K-V两种不同类型数据
	};

map和set中不设计迭代器,就设计红黑树的迭代器,map和set在它们类中复用一下红黑树的迭代器即可。
迭代器就是对节点地址的封装,加上迭代器常见的几种操作,map和set的迭代器都是双向迭代器,需要注意的是++迭代器是按照树的中序遍历来走的,迭代器- -操作就是中序的逆序。

template<class T, class Ref, class Ptr>  //迭代器常见的模板参数,T指明数据类型,
//Ref是指明引用返回的是const T 还是T ,Ptr指明是const T*,还是T*
struct RBiterator
{
	typedef RBTreeNode<T> Node;   //两个重命名,简写一下
	typedef RBiterator<T, Ref, Ptr> self;
public:
	RBiterator(Node*node)//迭代器构造函数用节点指针构造
		:it(node)
	{}
	Ref operator*()     //迭代器的解引用返回数据
	{
		return it->_data;
	}
	self& operator++()  //这里的++就是要按照中序顺序走的
	{
		if (it->_right)   //从当前节点走到中序的下一个节点,如果右孩子存在,就去右子树找最左端。
		{
			Node* cur = it->_right;
			while (cur->_left)
			{
				cur = cur->_left;
			}
			it = cur;
		}
		else   //右子树为空,往上走,如果当前节点是父亲的右孩子,还要继续往上走,
		//直到为空或者当前节点是父亲的左孩子,如果走到根以上说明已经是中序末端了
		{
			Node* parent = it->_parent;
			while (parent&& it == parent->_right)
			{
				it = parent;
				parent = it->_parent;
			}
			it = parent;
		}
		return *this;   //返回一个迭代器
	}
	self operator++(int)  //后置++类似
	{
		self ret= *this;
		if (it->_right)
		{
			Node* cur = it->_right;
			while (cur->_left)
			{
				cur = cur->_left;
			}
			it = cur;
		}
		else
		{
			Node* parent = it->_parent;
			while (parent && it == parent->_right)
			{
				it = parent;
				parent = it->_parent;
			}
			it = parent;
		}
		return ret;
	}
	self operator--(int) //--迭代器就按++的相反来操作
	{
		self ret = *this;
		if (it->_left)
		{
			Node* cur = it->_left;
			while (cur->_right)
			{
				cur = cur->_right;
			}
			it = cur;
		}
		else
		{
			Node* parent = it->_parent;
			while (parent && it == parent->_left)
			{
				it = parent;
				parent = it->_parent;
			}
			it = parent;
		}
		return ret;
	}
	self& operator--()
	{
		if (it->_left)
		{
			Node* cur = it->_left;
			while (cur->_right)
			{
				cur = cur->_right;
			}
			it = cur;
		}
		else
		{
			Node* parent = it->_parent;
			while (parent&&it==parent->_left)
			{
				it = parent;
				parent = it->_parent;
			}
			it = parent;
		}
		return *this;
	}
	Ptr operator->()
	{
		return  &it->_data;
	}
	bool operator!=(const self&t)const
	{
		return it != t.it;
	}
	bool operator==(const self& t)const
	{
		return it == t.it;
	}
	Node* it;
};
template<class K,class T,class KofT>  
class RBTree          //红黑树的实现,上层map和set就共用这一份红黑树代码
{
	typedef RBTreeNode<T> Node;
	
public:
	typedef RBiterator<T, T&, T*> iterator;
	typedef RBiterator<T, const T&, const T*> const_iterator;
	RBTree()
		:_root(nullptr)
	{}
················//这里就是红黑树的一些构造函数和拷贝函数,对红黑树常用的操作函数,下面一一讲解,map和set中各自有一棵红黑树在包装各种对红黑树的操作
}

实现了迭代器,我们再来看红黑树的插入和查找,可以先实现插入和查找,返回类型为bool就行了,按照map和set插入返回类型是pair,查找的返回类型是迭代器,现在我们只需对插入和查找的返回类型做一些修改就行了。

pair<iterator,bool> insert(const T& val)
	{
		if (_root == nullptr)
		{
			_root = new Node(val);
			_root->_co = Black;
			return pair<iterator,bool>(iterator(_root),true);  //返回pair<itreator,bool>
		}
		Node* parent = nullptr, * cur = _root;
		KofT KT;     //这里就是用到了仿函数,set的红黑树用set的仿函数,map
		//的红黑树用map的仿函数,都是为了把存储数据中的key取出来,比较大小,插入节点.
		while (cur)
		{
			if (KT(val) <KT(cur->_data))
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (KT(val) > KT(cur->_data))
			{
				parent = cur;
				cur = cur->_right;
			}
			else
				return pair<iterator, bool>(iterator(cur),false);
		}
		cur = new Node(val);
		Node* newnode = cur;    //返回插入节点的迭代器作为first的pair值,后序cur可能改变,所以先保持新节点地址
		if (KT(cur->_data) < KT(parent->_data))
		{
			parent->_left = cur;
		}
		else
			parent->_right = cur;
		cur->_parent = parent;
		while (parent && parent->_co == Red)               //更新颜色
		{
			Node* grandparent = parent->_parent;
			if (parent == grandparent->_left)
			{
				Node* uncle = grandparent->_right;
				if (uncle && uncle->_co == Red)
				{
					parent->_co = uncle->_co = Black;
					grandparent->_co = Red;
					cur = grandparent;
					parent = cur->_parent;
				}
				else // if(uncle == nullptr || uncle->_co == Black)      //g
				{                                                    //p
					if (parent->_left == cur)                      //c
					{
						RotateR(grandparent);         //右单旋  parent变黑色grandparent变红色
						grandparent->_co = Red;
						parent->_co = Black;
					}
					else                                            //g
					{                                             //p
						RotateL(parent);                             //c
						RotateR(grandparent);
						grandparent->_co = Red;
						cur->_co = Black;
					}
					break;
				}
			}
			else
			{
				Node* uncle = grandparent->_left;
				if (uncle && uncle->_co == Red)
				{
					parent->_co = uncle->_co = Black;
					grandparent->_co = Red;
					cur = grandparent;
					parent = cur->_parent;
				}
				else  //if(uncle == nullptr || uncle->_co == Black)  
				{
					if (parent->_right == cur)                                                   //g
					{                                                                              //p
						RotateL(grandparent);         //左单旋  parent变黑色grandparent变红色       //c
						grandparent->_co = Red;
						parent->_co = Black;
					}
					else                                       //g
					{                                            //p
						RotateR(parent);                       //c
						RotateL(grandparent);
						grandparent->_co = Red;
						cur->_co = Black;
					}
					break;
				}
			}
		}
		_root->_co = Black;
		return pair<iterator, bool>(iterator(newnode),true);  //返回pair
	}

查找操作就要用到红黑树的第一个模板参数了,因为我们就是按key值来查找的,查找的函数就只有一个key的函数参数,所以构建红黑树,就传一个key的模板参数。

iterator find(const K& val)const
	{
		Node* cur = _root;
		KofT KT;
		while (cur)
		{
			if (val < KT(cur->_data))
			{
				cur = cur->_left;
			}
			else if (val > KT(cur->_data))
			{
				cur = cur->_right;
			}
			else
				return iterator(cur);
		}
		return end();   //迭代器指向最后一位数据的后一位。
	}

红黑树的begin和end接口,上层map和set的begin和end就直接调用红黑树的begin和end接口。

iterator begin()
	{
		if (_root == nullptr)
			return iterator(nullptr);
		Node* cur = _root;
		while (cur->_left)
		{
			cur = cur->_left;
		}
		return iterator(cur);
	}
	iterator end()
	{
		return iterator(nullptr);
	}

还有判空empty和数据数量size两个接口也由红黑树实现,map和set直接调用即可

bool empty()const
	{
		return _root == nullptr;
	}
	size_t size()const
	{
		return _size(_root);
	}
	size_t _size(Node* root)const
	{
		if (root == nullptr)
			return 0;
		return _size(root->_left) + _size(root->_right) + 1;
	}

到这里map和set的主要接口就可以实现了

template<class K, class V>
	class map
	{
		
	public:
		struct mapKofT
		{
			const K& operator()(const pair<K,V>& val)
			{
				return val.first;
			}
		};
		typedef typename RBTree<K, pair<K, V>, mapKofT>::iterator iterator;
		iterator begin()
		{
			return _t.begin();
		}
		iterator end()
		{
			return _t.end();
		}
		iterator find(const K&val)
		{
			return _t.find(val);
		}
		pair<iterator,bool> insert(const pair<K, V>& val)
		{
			return _t.insert(val);
		}
		V& operator[](const K& k)
		{
			auto it = _t.insert(pair<K, V>(k, V()));
			return it.first->second;
		}
		bool empty()const
		{
			return _t.empty();
		}
		size_t size()const
		{
			return _t.size();
		}
	private:
		RBTree<K, pair<K,V>, mapKofT>_t;
	};
template<class K>
	 class set
	 {
	 public:
		 struct setKofT
		 {
			 const K& operator()(const K& val)
			 {
				 return val;
			 }
		 };
		 typedef typename RBTree<K, K, setKofT>::iterator iterator;
		 iterator begin()
		 {
			 return _t.begin();
		 }
		 iterator end()
		 {
			 return _t.end();
		 }
		 pair<iterator, bool> insert(const K& val)
		 {
			 return _t.insert(val);
		 }
		 iterator find(const K&val)
		 {
			 return _t.find(val);
		 }
		 bool empty()const
		 {
			 return _t.empty();
		 }
		 size_t size()const
		 {
			 return _t.size();
		 }
	 private:
		 RBTree<K, K, setKofT>_t;
	 };

可以看出map和set主要操作都是在红黑树中就实现了,只需给明红黑树需要的模板参数即可,我们也不必在map和set中实现拷贝构造,赋值操作,析构函数等,因为map和set中成员的数据就只有自定义类型的红黑树,我们同样只要实现红黑树的这些成员方法即可,它们会自动调用自定义类型相应的构造函数和析构函数。

RBTree(const RBTree<K,T,KofT>&tree)
	{
		_root = copy(tree._root);  //实现深拷贝
	}
	RBTree<K, T, KofT>& operator=(RBTree<K, T, KofT> t)
	{
		swap(_root, t._root);  //赋值方法现代写法
		return *this;
	}
	~RBTree()   //析构函数
	{
		destory(_root);
		_root = nullptr;   
	}
	Node* copy(Node* root)  
	{
		if (root == nullptr)
			return nullptr;
		Node* newnode = new Node(root->_data);  //一个个节点对应把数据拷贝创建新节点复刻
		newnode->_co = root->_co;   //颜色同样要拷贝
		newnode->_left = copy(root->_left);   //连接关系拷贝
		newnode->_right = copy(root->_right);
		if (newnode->_left)
			newnode->_left->_parent = newnode;  
		if (newnode->_right)
			newnode->_right->_parent = newnode;
		return newnode;
	}
	void destory(Node* root)
	{
		if (root == nullptr)
			return;          
		destory(root->_left);    //析构顺序是要按照后序来析构的
		destory(root->_right);
		delete root;
	}

至此就实现了一个简易的map和set。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值