用红黑树封装map&set【C++】

目录

前言

一,定义

二,完善set&map比较 

三,迭代器实现

operator++

operator--

operator[]

四,发现问题

解决

修改一: set封装层面

修改二:正向迭代器修改

下期预告: 哈希!!

结语


前言

我们在上篇文章红黑树——插入底层实现【C++】面试重灾区!!-CSDN博客

在上篇文章我们实现了红黑树的插入实现,同时也知道map与set底层一般是红黑树实现,那么本篇问文章我们来尝试封装map&set。

注意:建议完整学完本节,代码是逐步完善的。

首先我们从STL源码中寻找线索

还是比较明显的,红黑树的底层更加适合Map,但STL想让set复用map的红黑树,所以让set做出了一些 “ 妥协 ”:

 参数传递方向:

一,定义

根据STL源代码的提示,我们能初步形成框架

namespace my_map
{
	template <class K, class V>
	class map
	{
		RB_Tree<K, pair<const K, V>> _t;

	public:
		bool insert(const pair<const K, V>& p)
		{
			return _t.insert(p);
		}

	};
}
namespace my_set
{
	template <class K>
	class set
	{
		RB_Tree<K, K> _t;

	public:
		bool insert(const K& p)
		{
			return _t.insert(p);
		}
	};
}

同时我们要对红黑树的定义进行修改:

template <class T>
struct RBT_Data
{
	T _data;
	RBT_Data<T>* left = nullptr;
	RBT_Data<T>* right = nullptr;
	RBT_Data<T>* parent = nullptr;
	color _col;  // 颜色

	RBT_Data(const T& p)
		:_data(p)
		,_col(RED) //与其修改黑色路径数量,不如违反红子孩子都为黑的原则来的轻松。
	{}
};

template <class K, class V>
class RB_Tree 
{
	typedef RBT_Data<V> RBT_Data;
	RBT_Data* root = nullptr;
....

二,完善set&map比较 

从结果上来看,set&map 竟然运行成功。这是为什么呢??  没有了固定的  cur->_kv.first map应该是无法访问pair里面的数据,那又是如何进行比较大小,形成排序的呢??

答案在pair里面

pair类里面重载了比较大小的符号,但都是first,second之间进行比较,我们的排序需要依照Key值进行判断。那如何解决??

通过构建仿函数

set与map形成各自的仿函数,这样set就能适配map。

三,迭代器实现

基础框架,基本符号" * ", " -> " , " != "的重载。

这里以set封装为例 

// 在set封装上的处理
namespace my_set
{
	template <class K>
	class set
	{
		typedef  RB_Tree_Iterator<K, K&, K*> iterator;
        typedef  RB_Tree_Iterator<K, const K&, const K*> const_iterator;
		struct KeyofT
		{
			const K& operator()(const K& data)
			{
				return data;
			}
		};

		RB_Tree<K, K, KeyofT> _t;
	public:
		bool insert(const K& p)
		{
			return _t.insert(p);
		}

		iterator begin()
		{
			return _t.begin();
		}

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

// 迭代器类处理
template <class T, class Ref, class Ptr>
class RB_Tree_Iterator
{
	typedef RBT_Data<T>  RBT_Data;
	typedef RB_Tree_Iterator<T, Ref, Ptr> Seft;

	RBT_Data* it_root ;

public:
	RB_Tree_Iterator( RBT_Data* _node = nullptr)
	    :it_root(_node)
	{}

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

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

// 在RB_Tree上的处理
template <class K, class V, class KeyofT>
class RB_Tree
{
	typedef RB_Tree_Iterator<V, V&, V*> iterator;
	typedef RB_Tree_Iterator<V, const V&, const V*> const_iterator;
.......

	iterator begin()
	{
		RBT_Data* node = root;
		while (node && node->left)
		{
			node = node->left;	
		}
		return iterator(node);
	}
	
	// end跟以往的不同,end表示最后一个数据的下一个数据,也就是nullptr,实际距离是根的父节点。
	iterator end() 
	{
		return iterator(nullptr);
	}

	const const_iterator begin() const
	{
		RBT_Data* node = root;
		while (node && node->left)
		{
			node = node->left;
		}
		return const_iterator(node);
	}

	const const_iterator end() const
	{
		return const_iterator(nullptr);
	}
....

  

但我们还差一个极其重要的字符 “ ++ ”的实现,由于这个比较特殊我们分开单独讲。

operator++

思想:中序遍历来访问各个数据。比较难的是如何利用红黑树的三叉链,来实现中序遍历。

   Seft& operator++()
	{
		if (it_root && it_root->right)
		{
			// 假设右孩子存在
			RBT_Data* subleft = it_root->right;
			while (subleft->left)
			{
				subleft = subleft->left;
			}
			it_root = subleft;
		}
		else //如果没有右孩子,就向上回溯
		{
			RBT_Data* cur = it_root;
			RBT_Data* par = it_root->parent;
			while (par && par->right == cur)
			{
				cur = par;
				par = par->parent;
			}
				it_root = par;
		}
		return *this;
	}

operator--

 " -- "与 “ ++ ”两符号重载,本质上没多大改变,就是从原来的 左 ->  根  -> 右   =>    右 -> 根 -> 左

Seft& operator--()
	{
		if (it_root && it_root->right)
		{
			// 假设左侧存在
			RBT_Data* subright = it_root->left;
			while (subright->right)
			{
				subright = subright->right;
			}
			it_root = subright;
		}
		else //如果左侧没有
		{
			RBT_Data* cur = it_root;
			RBT_Data* par = it_root->parent;
			while (par && par->left == cur)
			{
				cur = par;
				par = par->parent;
			}
			it_root = par;
		}
		return *this;
	}

operator[]

这是STL对operator[] 的使用, 这需要我们对insert()进行返回值的修改,我们需要返回一个pair类型,里面放着pair<iterator, bool>,方便我们修改value的值。

这里我给一个案例进行修改:insert返回value的引用

后面的返回值处理成pair返回类似。

然后就是map与set的封装层,我们要如何使用呢??代码如下:

//  set     
    K& operator[](const K& key)
    {
	pair<iterator, bool> ret = _t.insert(key);
	if (ret.second)
		return ret.first.it_root->_data;
	cout << "插入失败" << endl;
		}
// map
V& operator[](const K& key)
{
   pair<iterator, bool> ret = _t.insert(make_pair(key, V()));
   if (ret.second)
     return ret.first->second;
   cout << "插入失败" << endl;
}

四,发现问题

 我们运行这段测试代码,我们会发现,set里面的数据被修改,但这是不被允许的。

    my_set::set<int> st;
	st[1];
	st[2];
	st[3];
	st[4];

	for (auto& e : st)
	{
		cout << e << " ";
	}

	for (auto& e : st)
	{
		e = 1;
		cout << e << " ";
	}

原因分析:下图是普通对象set获取迭代器参数的传递

解决

对于set的普通对象,如果是上面的代码,我们无法在适配map的前提下,保证set的value不可修改。解决思路:我们让set的普通迭代器类型底层是const迭代器。但其中的细节还是非常巧妙,我们通过下图讲解特殊之初吧。

修改一: set封装层面

修改二:正向迭代器修改

好啦,map与set封装精华就到这里了。

map_set.h

namespace my_set
{
	template <class K>
	class set
	{
		struct KeyofT
		{
			const K& operator()(const K& data)
			{
				return data;
			}
		};
		typedef  typename RB_Tree<K, K, KeyofT>::const_iterator iterator;  //直接用,const对象的迭代器充当普通迭代器
		typedef  typename RB_Tree<K, K, KeyofT>::const_iterator const_iterator;

		RB_Tree<K, K, KeyofT> _t;
	public:
		pair<iterator, bool> insert(const K& p)
		{
			return _t.insert(p);
		}

		K& operator[](const K& key)
		{
			pair<iterator, bool> ret = _t.insert(key);
			if (ret.second)
				return ret.first.it_root->_data;
			cout << "插入失败" << endl;
		}

		iterator begin()
		{
			return _t.begin();
		}

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

		const_iterator begin() const
		{
			return _t.begin();
		}

		const_iterator end() const
		{
			return _t.end();
		}
	};
}

namespace my_map
{
	template <class K, class V>
	class map
	{
		typedef  RB_Tree_Iterator<pair<const K, V>, pair<const K, V>&, pair<const K, V>*> iterator;
		typedef  RB_Tree_Iterator<pair<const K, V>, const pair<const K, V>&, const pair<const K, V>*> const_iterator;
		struct KeyofT
		{
			const K& operator()(const pair<const K, V>& data)
			{
				return data.first;
			}
		};

		RB_Tree<K, pair<const K, V>, KeyofT> _t;

	public:
		pair<iterator, bool> insert(const pair<const K, V>& p)
		{
			return _t.insert(p);
		}

		V& operator[](const K& key)
		{
			pair<iterator, bool> ret = _t.insert(make_pair(key, V()));
			if (ret.second)
				return ret.first->second;
			cout << "插入失败" << endl;
		}
		iterator begin()
		{
			return _t.begin();
		}

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

		const_iterator begin() const
		{
			return _t.begin();
		}

		const_iterator end() const
		{
			return _t.end();
		}
	};
}

RB_Tree.h

#include <iostream>
#include<string>
#include <assert.h>
#include <utility>
using namespace std;

enum color
{
	RED,
	BLACK
};


template <class T>
struct RBT_Data
{
	T _data;
	RBT_Data<T>* left = nullptr;
	RBT_Data<T>* right = nullptr;
	RBT_Data<T>* parent = nullptr;
	color _col;  // 颜色

	RBT_Data(const T& p)
		:_data(p)
		, _col(RED) //与其修改黑色路径数量,不如违反红子孩子都为黑的原则来的轻松。
	{}
};

template <class T, class Ref, class Ptr>
class RB_Tree_Iterator
{
	typedef RBT_Data<T>  RBT_Data;
	typedef RB_Tree_Iterator<T, Ref, Ptr> Self;
	typedef RB_Tree_Iterator<T, T&, T*> iterator;  // 跟上面的不同,这个iterator只以T为准,这样在set const的对象时,不会出现
	                                               // 两个const 的情况

public:
	RBT_Data* it_root ;

	RB_Tree_Iterator(RBT_Data* _node = nullptr)
		:it_root(_node)
	{}

	RB_Tree_Iterator(const iterator& it)
		:it_root(it.it_root)
	{}

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

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

	// node* 地址也算不同罢了
	bool operator!= (const Self& node)
	{
		return node.it_root != it_root;
	}

	Self& operator++()
	{
		if (it_root && it_root->right)
		{
			// 假设右侧存在
			RBT_Data* subleft = it_root->right;
			while (subleft->left)
			{
				subleft = subleft->left;
			}
			it_root = subleft;
		}
		else //如果右侧没有
		{
			RBT_Data* cur = it_root;
			RBT_Data* par = it_root->parent;
			while (par && par->right == cur)
			{
				cur = par;
				par = par->parent;
			}
			it_root = par;
		}
		return *this;
	}


	Self& operator--()
	{
		if (it_root && it_root->right)
		{
			// 假设左侧存在
			RBT_Data* subright = it_root->left;
			while (subright->right)
			{
				subright = subright->right;
			}
			it_root = subright;
		}
		else //如果左侧没有
		{
			RBT_Data* cur = it_root;
			RBT_Data* par = it_root->parent;
			while (par && par->left == cur)
			{
				cur = par;
				par = par->parent;
			}
			it_root = par;
		}
		return *this;
	}

};


template <class K, class V, class KeyofT>
class RB_Tree
{
public:
	typedef RBT_Data<V> RBT_Data;
	typedef RB_Tree_Iterator<V, V&, V*> iterator;
	typedef RB_Tree_Iterator<V, const V&, const V*> const_iterator;

	// 这个也是按照后序遍历方式进行析构
	void Destroy(RBT_Data* node)
	{
		if (node == nullptr)
			return;
		Destroy(node->left);
		Destroy(node->right);
		delete(node);
	}

	~RB_Tree()
	{
		if (root)
		{
			Destroy(root);
			root = nullptr;
		}
	}

	iterator begin()
	{
		RBT_Data* node = root;
		while (node && node->left)
		{
			node = node->left;
		}
		return iterator(node);
	}

	// end跟以往的不同,end表示最后一个数据的下一个数据,也就是nullptr
	iterator end()
	{
		return iterator(nullptr);
	}


	const_iterator begin() const
	{
		RBT_Data* node = root;
		while (node && node->left)
		{
			node = node->left;
		}
		return const_iterator(node);
	}

	const_iterator end() const
	{
		return const_iterator(nullptr);
	}

	void Print()
	{
		_Print(root);
	}

	bool IsBalance()
	{
		if (root && root->_col == RED)
		{
			return false;
		}

		int BlackNum = 0;
		// 还差一个最长路径
		int standard = 0;
		RBT_Data* cur = root;
		while (cur)
		{
			if (cur->_col == BLACK)
				standard++;
			cur = cur->left;
		}
		return _IsBalance(root->left, BlackNum, standard) && _IsBalance(root->right, BlackNum, standard);
	}

	pair<iterator, bool>  insert(const V& p)
	{
		RBT_Data* new_a_d = new RBT_Data(p);
		if (!root)
		{
			root = new_a_d;
			root->_col = BLACK;
			return make_pair(iterator(new_a_d), true);
		}
		else
		{
			RBT_Data* cur = root;
			RBT_Data* parent = nullptr;
			KeyofT Kt;
			while (cur)
			{
				if (Kt(p) < Kt(cur->_data))
				{
					parent = cur;
					cur = cur->left;
				}
				else if (Kt(p) > Kt(cur->_data))
				{
					parent = cur;
					cur = cur->right;
				}
				else
				{
					delete(new_a_d); // 插入失败,删除新建结点
					return make_pair(iterator(root), false);
				}
			}

			if (p < parent->_data)
			{
				parent->left = new_a_d;
			}
			else
			{
				parent->right = new_a_d;
			}
			new_a_d->parent = parent;

			// 调整颜色
			cur = new_a_d;
			RBT_Data* par = cur->parent;
			if (cur == root)
			{
				cur->_col = BLACK;
			}

			while (par && par->_col == RED)
			{
				RBT_Data* gf = par->parent;
				RBT_Data* uncle = nullptr;
				if (gf && par == gf->right)
				{
					uncle = gf->left;
				}
				else if (gf && par == gf->left)
				{
					uncle = gf->right;
				}
				else
				{
					assert(false);
				}


				if (uncle && uncle->_col == RED)// 有u且为红
				{
					gf->_col = RED;
					uncle->_col = BLACK;
					par->_col = BLACK;

					cur = gf;  // 切换为祖先,进入循环向上
					par = cur->parent;
				}
				else if (!uncle ||
					(uncle && uncle->_col == BLACK))
				{   // 情况2 + 3,判断,是否是折线还是直线
					if (gf->left == par && par->left == cur)
					{  // 右单选
						RotateR(gf);
					}
					else if (gf->right == par && par->right == cur)
					{  // 左单旋
						RotateL(gf);
					}
					else if (gf->left == par && par->right == cur)
					{  // 需要左双旋
						RotateLR(gf);
					}
					else if (gf->right == par && par->left == cur)
					{  // 需要右双旋
						RotateRL(gf);
					}
					else
					{
						assert(false);
					}
					break;
				}
				else
				{
					assert(false);
				}
			}

			if (root->_col == RED)
			{
				root->_col = BLACK;
			}
			return make_pair(iterator(new_a_d), true);
		}
	}

private:
		RBT_Data* root = nullptr;

		void RotateL(RBT_Data* parent)
		{
			assert(parent->right);
			RBT_Data* par = parent;
			RBT_Data* par_R = par->right;
			RBT_Data* par_RL = par->right->left;
			RBT_Data* ppnode = par->parent;

			par->right = par_RL;
			if (par_RL)
				par_RL->parent = par;

			par_R->left = par;
			par->parent = par_R;
			par_R->parent = ppnode;

			if (!ppnode)
			{
				root = par_R;
			}
			else if (ppnode->left == par)
			{
				ppnode->left = par_R;
			}
			else
			{
				ppnode->right = par_R;
			}

			par->_col = RED;
			par_R->_col = BLACK;
		}

		void RotateR(RBT_Data* parent)
		{
			assert(parent->left);
			RBT_Data* par = parent;
			RBT_Data* par_L = par->left;
			RBT_Data* par_LR = par->left->right;
			RBT_Data* ppnode = par->parent;

			par->left = par_LR;
			if (par_LR)
				par_LR->parent = par;

			par_L->right = par;
			par->parent = par_L;
			par_L->parent = ppnode;

			if (!ppnode)
			{
				root = par_L;
			}
			else
			{
				if (ppnode->left == par)
				{
					ppnode->left = par_L;
				}
				else
				{
					ppnode->right = par_L;
				}
			}

			par->_col = RED;
			par_L->_col = BLACK;
		}

		void RotateLR(RBT_Data* parent)
		{
			assert(parent->left);
			RBT_Data* par = parent;
			RBT_Data* par_L = par->left;
			RBT_Data* par_LR = par->left->right;

			RotateL(par_L);
			RotateR(par);

			par_LR->_col = BLACK;
			par_L->_col = BLACK;
		}

		void RotateRL(RBT_Data* parent)
		{
			assert(parent->right);
			RBT_Data* par = parent;
			RBT_Data* par_R = par->right;
			RBT_Data* par_RL = par->right->left;

			RotateR(par_R);
			RotateL(par);

			par_RL->_col = BLACK;
			par_R->_col = BLACK;
		}

		void _Print(const RBT_Data* root)
		{
			if (root == nullptr)
			{
				// cout << "[]";
				return;
			}
			_Print(root->left);
			cout << KeyofT(root->_data) << " ";
			_Print(root->right);
		}

		bool _IsBalance(const RBT_Data* cur, int BlackNum, int standard)
		{
			if (cur == nullptr)
			{
				return true;
			}
			if (cur->_col == BLACK)
				BlackNum++;

			if (cur->_col == RED && cur->_col == cur->parent->_col)
			{
				return false;
			}

			return  _IsBalance(cur->left, BlackNum, standard) && _IsBalance(cur->right, BlackNum, standard);
		}
};

下期预告: 哈希!!

结语

   本小节就到这里了,感谢小伙伴的浏览,如果有什么建议,欢迎在评论区评论,如果给小伙伴带来一些收获请留下你的小赞,你的点赞和关注将会成为博主创作的动力

  • 15
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 16
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值