c++——map和set的封装

注:该封装基于前面博客已实现红黑树,map和set封装并不难,主要还是对红黑树的理解


目录

一. 改造红黑树

1. 改变节点的定义,使用更高维度的泛型

2. 红黑树追加迭代器的实现

1. 红黑树迭代器的构造函数和基本框架

2. begin()和end()的实现

3. operator*和operator->的实现

4. operator++和operator--的实现

5. operator!=和operator==的实现

3. 对RBTree类进行改变

1. 改造insert

2. 增加find的实现

4. 改造后的RBTree树代码展示

二. 封装set

三. 封装map


一. 改造红黑树

颜色定义不更改,但是由于我们是使用一颗红黑树同时来作为封装set和map的底层结构,因此对数据类型需要进行处理,这里参考stl源码给出的解决方式是使用更高维度的泛型,因此我们也定义成更高维度的泛型,即数据类型这里我们使用T来接受va类型。

即我们不再使用KV的形式接受数据,而是用T来接受数据,T决定红黑树存什么数据。

这里和节点定义一样,我们使用T来接受数据,T决定红黑树存什么数据,对于set来说RBTree<K, V>传进来的是K,对于map来说RBTree<K, pair<K, V>>传进来的是pair类型。

虽然用T能解决传进来不同的数据类型,但是由于我们需要取K,对于set来说K就是K,但是,对于map来说是pair的第一个参数,所以由于无法确定T是K还是pair类型,无法准确去取出来T中的类型。

根据官方库可以发现再引进来一个模板参数KeyOfT,用来取出T中的类型,而这个KeOfT传给RBTree的,查看set和map后源码后发现KeyOfT其实是一个仿函数,用来提取T中类型的K的,因此我们就知道RBTree应该有三个模板参数,分别是K,T和KeyOfT

1. 改变节点的定义,使用更高维度的泛型

有了以上推论,我们可以对节点定义进行改变

//改变红黑节点的定义,使用更高维度的泛型
template<class T>
struct RBTreeNode
{
	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right;
	RBTreeNode<T>* _parent;
	T _data;
	color _col;

	//构造函数
	RBTreeNode(const T& data)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_data(data)
		,_col(RED)
	{}
};

这里使用T来标识数据类型,因为节点可能是K类型的(set传过来的),也可能是pair类型的(map传过来的)

2. 红黑树追加迭代器的实现

由于map和set都需要用到迭代器,但是两者的迭代器实际上都是封装了红黑树的迭代器,因此我们要先实现红黑树的迭代器,这里迭代器的实现和链表极其相似,因此,也是使用三个模板参数,分别代表普通类型,引用类型,指针类型,库里的实现是采用了哨兵卫的头节点的方式,但是这里并不这样做

1. 红黑树迭代器的构造函数和基本框架

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)
	{}

	operator*()
	{}

	operator->()
	{}

	operator++()
	{}

	operator++(int)
	{}

	operator--()
	{}

	operator--(int)
	{}

	operator!=(const Self& s) const
	{}

	operator==(const Self& s) const
	{}

};

和链表迭代器的实现基本一致,将迭代器的基本功能实现

2. begin()和end()的实现

由于迭代器访问的顺序是按照二叉平衡搜索树的中序的顺序访问的,因此我们也不能坏这个规则。

按中序访问的思路,左子树->根->右子树:

begin应该返回中序的第一个元素,即需要去找二叉平衡搜索树的最左节点:

    iterator Begin()
	{
		Node* subleft = _root;
		while (subleft && subleft->_left)
		{
			subleft = subleft->_left;
		}

		return iterator(subleft);
	}

由于迭代器是自己实现的类,因此应该用迭代器创建对象并返回

 end()应该返回的是nullptr,因为最后遍历完成后应该是遍历到最右节点的nullptr节点

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

直接用迭代器创建一个对象并传nullptr

3. operator*和operator->的实现

和链表一样,operator*返回节点中的数据operator->返回节点数据的地址(原因已在链表迭代器实现已解释,不做过多赘述)

    Ref operator*()
	{
		return _node->_data;
	}

	Ptr operator->()
	{
		return &_node->_data;
	}

4. operator++和operator--的实现

由于begin(),是按照中序的方式访问的,++可以分为两种情况,当右子树不存在为空时,找孩子是祖先的左的那个祖先节点,当右子树存在不为空时,找右子树的最左节点

有如下图红黑树:

一开始begin()迭代器应该位于节点为1处,当我们要++遍历这颗红黑树时:

由于1的右子树不为空,找右子树的最左节点,最左节点为空,则++以后访问到是6,由于6的右子树为空,再++应该去找孩子是祖先的左的那个祖先节点,此时找到就是节点为8的祖先节点,此时发现,这就已经按中序的方式运行起来了

++迭代器的实现方式如下代码:

    Self& operator++()
	{
		if (_node->_right == nullptr)
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && parent->_right == cur)
			{
				cur = parent;
				parent = parent->_parent;
			}

			_node = parent;
		}
		else
		{
			Node* subleft = _node->_right;
			while (subleft->_left)
			{
				subleft = subleft->_left;
			}

			_node = subleft;
		}

		return *this;
	}

	Self operator++(int)
	{
		Self tmp(*this);
		++(*this);

		return tmp;
	}

由于begin(),是按照中序的方式访问的,--可以分为两种情况,当左子树不存在为空时,找孩子是祖先的右的那个祖先节点,当左子树存在不为空时,找左子树的最右节点

以下图红黑树为例:

假设此时迭代器已经遍历到节点为11的地方:

当我们要--遍历红黑树时,由于11的左子树不存在为空,往上找孩子是祖先的右的祖先节点,我发现11就是8的右,于是迭代器访问的是节点8的祖先节点,再次--,此时节点8的左子树存在不为空,找左子树的最右节点,此时节点6是1的最右节点,因此找到的节点是节点6,此时,发现已经是在按中序的方式遍历了

--的迭代器的实现代码如下:

    Self& operator--()
	{
		if (_node->_left == nullptr)
		{
			// 找祖先里面,孩子是父亲
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_left)
			{
				cur = cur->_parent;
				parent = parent->_parent;
			}

			_node = parent;
		}
		else
		{
			// 左子树的最右节点
			Node* subRight = _node->_left;
			while (subRight->_right)
			{
				subRight = subRight->_right;
			}

			_node = subRight;
		}

		return *this;
	}

	Self operator--(int)
	{
		Self tmp(*this);

		--(*this);

		return tmp;
	}

5. operator!=和operator==的实现

比两个对象中的节点即可

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

	bool operator==(const Self& s) const
	{
		return _node == s->_node;
	}

至此迭代器的基本实现已完成

3. 对RBTree类进行改变

由于模板参数的改变和迭代器的实现,使RBTree的实现更加完整,需要对RBTree类进行改变。

1. 改造insert

首先就是对insert进行改变,有了迭代器的实现,我们应该和库里面的一样,返回的是pair<iterator, bool>而不是bool,以及在比较的时候应该使用KeyOfT这个仿函数模板参数来接受K,修改完成代码如下:

    pair<iterator, bool> Insert(const T& data)
	{
		// 1、搜索树的规则插入
		// 2、看是否违反平衡规则,如果违反就需要处理:旋转
		if (_root == nullptr)
		{
			_root = new Node(data);
			_root->_col = BLACK;
			return make_pair(iterator(_root), 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 make_pair(iterator(cur), true);
			}
		}

		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;

		// 存在连续红色节点
		while (parent && parent->_col == RED)
		{
			Node* grandfater = parent->_parent;
			assert(grandfater);

			if (grandfater->_left == parent)
			{
				Node* uncle = grandfater->_right;
				// 情况一:
				if (uncle && uncle->_col == RED) // 叔叔存在且为红
				{
					// 变色
					parent->_col = uncle->_col = BLACK;
					grandfater->_col = RED;

					// 继续往上处理
					cur = grandfater;
					parent = cur->_parent;
				}
				else // 叔叔不存在 或者 叔叔存在且为黑
				{
					if (cur == parent->_left) // 单旋
					{
						//     g
						//   p
						// c
						RotateR(grandfater);
						parent->_col = BLACK;
						grandfater->_col = RED;
					}
					else // 双旋
					{
						//     g
						//   p
						//     c 
						RotateL(parent);
						RotateR(grandfater);
						cur->_col = BLACK;
						grandfater->_col = RED;
					}

					break;
				}
			}
			else //(grandfater->_right == parent)
			{
				Node* uncle = grandfater->_left;
				// 情况一:
				if (uncle && uncle->_col == RED)
				{
					// 变色
					parent->_col = uncle->_col = BLACK;
					grandfater->_col = RED;

					// 继续往上处理
					cur = grandfater;
					parent = cur->_parent;
				}
				else
				{
					if (cur == parent->_right)
					{
						// g
						//   p
						//     c 
						RotateL(grandfater);
						parent->_col = BLACK;
						grandfater->_col = RED;
					}
					else // 双旋
					{
						// g
						//   p
						// c
						RotateR(parent);
						RotateL(grandfater);
						cur->_col = BLACK;
						grandfater->_col = RED;
					}

					break;
				}
			}
		}

		_root->_col = BLACK;

		return make_pair(iterator(newnode), true);//创建并返回iterator对象
	}

相比原来的insert对返回值做了修改,并用KeyOfT仿函数创建对象来接受其中的K,来进行比较

2. 增加find的实现

利用KeyOfT提取T中的K来进行比较找想要找的元素,找到返回迭代器元素位置的迭代器否则返回end(),具体代码实现如下:

    iterator Find(const K& key)
	{
		Node* cur = _root;
		KeyOfT kot;
		while (cur)
		{
			if (kot(cur->_data) < key)
			{
				cur = cur->_right;
			}
			else if (kot(cur->_data) > key)
			{
				cur = cur->_left;
			}
			else
			{
				return iterator(cur);
			}
		}

		return End();
	}

4. 改造后的RBTree树代码展示

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)
		:_data(data)
		, _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _col(RED)
	{}
};

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;
	}
	// 休息17:00
	Self& operator++()
	{
		if (_node->_right == nullptr)
		{
			// 找祖先里面,孩子是父亲左的那个
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && parent->_right == cur)
			{
				cur = cur->_parent;
				parent = parent->_parent;
			}

			_node = parent;
		}
		else
		{
			// 右子树的最左节点
			Node* subLeft = _node->_right;
			while (subLeft->_left)
			{
				subLeft = subLeft->_left;
			}

			_node = subLeft;
		}
		
		return *this;
	}

	Self operator++(int)
	{
		Self tmp(*this);
		
		++(*this);

		return tmp;
	}

	Self& operator--()
	{
		if (_node->_left == nullptr)
		{
			// 找祖先里面,孩子是父亲
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_left)
			{
				cur = cur->_parent;
				parent = parent->_parent;
			}

			_node = parent;
		}
		else
		{
			// 左子树的最右节点
			Node* subRight = _node->_left;
			while (subRight->_right)
			{
				subRight = subRight->_right;
			}

			_node = subRight;
		}

		return *this;
	}

	Self operator--(int)
	{
		Self tmp(*this);

		--(*this);

		return tmp;
	}

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

	bool operator==(const Self& s) const
	{
		return _node == s->_node;
	}
};

// T决定红黑树存什么数据
// set  RBTree<K, K>
// map  RBTree<K, pair<K, V>>
// KeyOfT -> 支持取出T对象中key的仿函数
template<class K, class T, class KeyOfT>
class RBTree
{
	typedef RBTreeNode<T> Node;
public:
	typedef __RBTreeIterator<T, T&, T*> iterator;
	typedef __RBTreeIterator<T, const T&, const T*> const_iterator;

	// 构造 拷贝构造 赋值 和析构 跟搜索树实现方式是一样的

	iterator Begin()
	{
		Node* subLeft = _root;
		while (subLeft && subLeft->_left)
		{
			subLeft = subLeft->_left;
		}

		return iterator(subLeft);
	}

	iterator 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);
	}

	pair<iterator, bool> Insert(const T& data)
	{
		// 1、搜索树的规则插入
		// 2、看是否违反平衡规则,如果违反就需要处理:旋转
		if (_root == nullptr)
		{
			_root = new Node(data);
			_root->_col = BLACK;
			return make_pair(iterator(_root), 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 make_pair(iterator(cur), true);
			}
		}

		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;

		// 存在连续红色节点
		while (parent && parent->_col == RED)
		{
			Node* grandfater = parent->_parent;
			assert(grandfater);

			if (grandfater->_left == parent)
			{
				Node* uncle = grandfater->_right;
				// 情况一:
				if (uncle && uncle->_col == RED) // 叔叔存在且为红
				{
					// 变色
					parent->_col = uncle->_col = BLACK;
					grandfater->_col = RED;

					// 继续往上处理
					cur = grandfater;
					parent = cur->_parent;
				}
				else // 叔叔不存在 或者 叔叔存在且为黑
				{
					if (cur == parent->_left) // 单旋
					{
						//     g
						//   p
						// c
						RotateR(grandfater);
						parent->_col = BLACK;
						grandfater->_col = RED;
					}
					else // 双旋
					{
						//     g
						//   p
						//     c 
						RotateL(parent);
						RotateR(grandfater);
						cur->_col = BLACK;
						grandfater->_col = RED;
					}

					break;
				}
			}
			else //(grandfater->_right == parent)
			{
				Node* uncle = grandfater->_left;
				// 情况一:
				if (uncle && uncle->_col == RED)
				{
					// 变色
					parent->_col = uncle->_col = BLACK;
					grandfater->_col = RED;

					// 继续往上处理
					cur = grandfater;
					parent = cur->_parent;
				}
				else
				{
					if (cur == parent->_right)
					{
						// g
						//   p
						//     c 
						RotateL(grandfater);
						parent->_col = BLACK;
						grandfater->_col = RED;
					}
					else // 双旋
					{
						// g
						//   p
						// c
						RotateR(parent);
						RotateL(grandfater);
						cur->_col = BLACK;
						grandfater->_col = RED;
					}

					break;
				}
			}
		}

		_root->_col = BLACK;

		return make_pair(iterator(newnode), true);
	}

	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		parent->_right = subRL;
		if (subRL)
			subRL->_parent = parent;

		Node* ppNode = parent->_parent;

		subR->_left = parent;
		parent->_parent = subR;

		if (parent == _root)
		{
			_root = subR;
			_root->_parent = nullptr;
		}
		else
		{
			if (parent == ppNode->_left)
			{
				ppNode->_left = subR;
			}
			else
			{
				ppNode->_right = subR;
			}

			subR->_parent = ppNode;
		}
	}

	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		parent->_left = subLR;
		if (subLR)
			subLR->_parent = parent;

		Node* ppNode = parent->_parent;

		subL->_right = parent;
		parent->_parent = subL;

		if (parent == _root)
		{
			_root = subL;
			_root->_parent = nullptr;
		}
		else
		{
			if (ppNode->_left == parent)
			{
				ppNode->_left = subL;
			}
			else
			{
				ppNode->_right = subL;
			}
			subL->_parent = ppNode;
		}
	}

	iterator Find(const K& key)
	{
		Node* cur = _root;
		KeyOfT kot;
		while (cur)
		{
			if (kot(cur->_data) < key)
			{
				cur = cur->_right;
			}
			else if (kot(cur->_data) > key)
			{
				cur = cur->_left;
			}
			else
			{
				return iterator(cur);
			}
		}

		return End();
	}

private:
	Node* _root = nullptr;
};

二. 封装set

set的底层结构就是红黑树,因此在set中直接封装一棵红黑树,然后将其接口包装下即可

由于在RBTree中传了KeyOfT来获取K,因此我们在set中需要实现一个仿函数来获取set的K

    template<class K>
	class set
	{
		//给红黑树获取K
		struct SetkeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};

其他都是包装RBTree的接口即可

注意:

由于set的Key是不能改变的,所以set的iterator应该用const_iterator包装。

但是会进一步导致insert函数在返回时,由于RBTree的iterator里的参数没有被const修饰是普通迭代器,但是set的iterator是const_iterator,就会出现iterator向const_iterator的转化,这是两个封装后毫无关系的类型,无法直接转化,因此我们需要先提取出来返回值稍作修改后重新封装,因此insert应该做出如下改造:

pair<iterator, bool> insert(const K& key)
{
    //pair<typename RBTree<K, K, SetkeyOfT>::iterator, bool> ret = _t.Insert(key);
	auto ret = _t.Insert(key);
	return pair<iterator, bool>(iterator(ret.first._node), ret.second);
}

注意:

这里和list实现迭代器一样,当我们typedef迭代器时,会出现内嵌类型的问题,需要使用到typename关键字

set具体封装的实现代码:

#include "RBTree.h"

namespace mystl
{
	template<class K>
	class set
	{
		//给红黑树获取K
		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;

		//set不能修改,只提供const版本
		iterator begin() const
		{
			return _t.Begin();
		}

		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);
			auto ret = _t.Insert(key);
			return pair<iterator, bool>(iterator(ret.first._node), ret.second);
		}

		iterator find(const K& key)
		{
			return _t.find(key);
		}

	private:
		RBTree<K, K, SetkeyOfT> _t;
	};
}

三. 封装map

这里需要的注意事项和set,唯一不同的是这里insert没有set的问题,因为map的iterator就是RBTree的普通迭代器封装的,因map的V是可以更改的,map和set的K是不能更改的,所以map的普通迭代器应该封装成普通的迭代器供V改变,因此,insert可以直接返回封装的insert的返回值。

map实现了operator[],这里也可以实现,直接封装RBTree的insert,返回值是RBTree返回值的V的引用供修改即可

实现代码如下:

    V& operator[](const K& key)
		{
			pair<iterator, bool> ret = insert(make_pair(key, V()));

			return ret.first->second;
		}

其他直接封装RBTree的接口即可,j具体实现代码如下:
 

#include "RBTree.h"

namespace mystl
{
	template<class K, class V>
	class map
	{
		//给红黑树获取K
		struct MapkeyOfT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};

	public:

		typedef typename RBTree<K, pair<K, V>, MapkeyOfT>::iterator iterator;
		typedef typename RBTree<K, pair<K, V>, MapkeyOfT>::const_iterator const_iterator;

		//map可以修改都提供
		iterator begin()
		{
			return _t.Begin();
		}

		iterator end()
		{
			return _t.End();
		}

		pair<iterator, bool> insert(const pair<K, V>& kv)
		{
			return _t.Insert(kv);
		}

		iterator find(const K& key)
		{
			return _t.find(key);
		}

		V& operator[](const K& key)
		{
			pair<iterator, bool> ret = insert(make_pair(key, V()));

			return ret.first->second;
		}

	private:
		RBTree<K, pair<K, V>, MapkeyOfT>  _t;
	};
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hiland.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值