[ C++ ] STL---map与set的模拟实现

本文详细讨论了红黑树模板的设计,包括节点定义、迭代器的实现、如何适应set和map容器的需求,以及插入、查找逻辑和红黑树性质的维护。特别关注了红黑树模板中第一个模板参数K的重要性,以及如何通过仿函数处理不同数据类型的比较。
摘要由CSDN通过智能技术生成

目录

前言:

 红黑树结点的定义

 红黑树的改造

红黑树的迭代器

operator++()

operator--()

map的模拟实现

operator [ ]

set的模拟实现

红黑树底层结构


前言:

set容器中存储的数据为键值key,map容器中存储的数据为键值对<key,value>,当红黑树的底层采用kv模型结构时,利用模版控制红黑树模型结构的第二个模版参数从而传递不同的数据类型使得set容器存储<key,key>map容器存储<key,pair<key,value>>,最终达到使用同一颗红黑树同时封装set容器与map容器的目的;

思考:红黑树第一个模板参数K是否可以省略?

  • 若省略红黑树的第一个模版参数,对于set容器,由于第二个模版参数与第一个模版参数相同,没有任何的影响;
  • 若省略红黑树的第一个模版参数,对于map容器,可以通过数据类型pair<K, V> 取到键值key,但是并不能取出key的类型,而红黑树的Find()函数需要key的数据类型,因此红黑树的第一个模版参数K不可以省略;

 红黑树结点的定义

enum Color
{
	RED,
	BLACK
};
template<class T>
struct RBTreeNode
{
	RBTreeNode<T>* _left;//指向左孩子
	RBTreeNode<T>* _right;//指向右孩子
	RBTreeNode<T>* _parent;//指向父节点
	Color _col;//结点颜色
	T _data;//存储有效数据
	RBTreeNode(const T& data)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _col(RED)
		, _data(data)
	{}
};

 红黑树的改造

红黑树的插入、查找等逻辑需要按照搜索二叉树的规则查找合适的插入位置需要使用键值key进行比较,set容器可以直接使用key(data)进行比较,但是map容器中的数据pair<key,value>(data)首先是按first比较,若first相等则按second比较,不满足只按照first比较的需求,因此在红黑树中设计第三个模版参数用于接收map与set容器中所提供的不同的比较方式;

set容器

template<class K>
class set
{
	//仿函数
	struct SetKeyOfT//获取set容器中数据的键值key
	{
		const K& operator()(const K& key)
		{
			return key;
		}
	};
public:
	//...
private:
	RBTree<K, K, SetKeyOfT> _t;
};

map容器

template<class K,class V>
class map
{
	//仿函数
	struct MapKeyOfT//获取map容器中数据的键值key
	{
		const K& operator()(const pair<K,V>& kv)
		{
			return kv.fisrt;
		}
	};
public:
	//......
private:
	RBTree<K, pair<K, V>, MapKeyOfT> _t;
};

RBTree

template <class K, class T,class KeyOfT>
class RBTree
{
	typedef RBTreeNode<T> Node;
public:
    //...
private:
    Node* _root=nullptr;
};

红黑树内部具体某个模块涉及到到数据比较时,便可通过定义仿函数对象的方式达到对于不同类型的数据,按照自定义的方式比较;

示例

iterator* Find(const T& data)
{
	KeyOfT kot;//定义仿函数对象
	Node* cur = _root;
	while (cur)
	{
		if (kot(cur->_data) < kot(data))
		{
			cur = cur->_right;
		}
		else if (kot(cur->_data)  > kot(data))
		{
			cur = cur->_left;
		}
		else
		{
			return cur;
		}
	}
	return nullptr;
}

红黑树的迭代器

思考一:

若it为结点指针,++it,能否从红黑树的当前位置指向中序遍历后的当前位置的下一位置?  (×)

思考二:

若it为结点指针,* it,能否得到红黑树结点中的数据_data?(×

只能将结点指针封装为自定义类型,从而使用运算符重载满足需求

迭代器的成员变量 : Node* _node;(_node为指向当前结点的指针)
迭代器的成员函数 : 运算符重载函数;

无论是普通迭代器还是const迭代器,均需要迭代器遍历容器中的内容,因此迭代器本身可以被修改,区别仅在于迭代器指向的内容是否可以被修改由于const迭代器本质为保护迭代器指向的内容不允许被修改,若实现const迭代器类,只需要普通迭代器的operator*()与operator->()两个接口的返回值采用const修饰,便保护容器中的内容不会被修改其余接口均保持不变;采用迭代器类增加两个模版参数,使用时便可实例化出普通迭代器与const迭代器

operator++()

operator--()

template<class T,class Ptr,class Ref>
struct RBTreeIterator
{
	typedef RBTreeNode<T> Node;
	typedef RBTreeIterator<T, Ptr, Ref> Self;
	Node* _node;

	//构造函数
	RBTreeIterator(Node* node)
		:_node(node)
	{}

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

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

	//++it
	Self& operator++()
	{
		//it指向当前结点,若右子树不为空,下一个访问的是右子树的最左结点
		if (_node->_right != nullptr)
		{
			Node* SubLeft = _node->_right;
			while (SubLeft->_left!= nullptr)
			{
				SubLeft = SubLeft->_left;
			}
			_node = SubLeft;
		}
		//it指向当前结点,若右子树为空,寻找祖先结点中某个结点是其父亲的左子树的结点
		else
		{
			Node* parent = _node->_parent;
			Node* cur = _node;
			while (parent&&parent->_right == cur)
			{
				cur = parent;
				parent = cur->_parent;
			}
			_node = parent;
		}

		return *this;
	}
	//it++
	Self operator++(int)
	{
		//记录++之前的it位置
		Self tmp = Self(_node);
		//it指向当前结点,若右子树不为空,下一个访问的是右子树的最左结点
		if (_node->_right != nullptr)
		{
			Node* SubLeft = _node->_right;
			while (SubLeft->_left != nullptr)
			{
				SubLeft = SubLeft->_left;
			}
			_node = SubLeft;
		}
		//it指向当前结点,若右子树为空,寻找祖先结点中某个结点是其父亲的左子树的结点
		else
		{
			Node* parent = _node->_parent;
			Node* cur = _node;
			while (parent&&parent->_right == cur)
			{
				cur = parent;
				parent = cur->_parent;
			}
			_node = parent;
		}
		return tmp;
	}

	Self& operator--()
	{
		if (_node == nullptr)
		{
			//特殊处理(未找到解决方案,具体参见带有哨兵位的红黑树结构)
		}
		//it指向当前结点,若左子树不为空,下一个访问的是左子树的最右结点
		if (_node->_left != nullptr)
		{
			Node* SubRight = _node->_left;
			while (SubRight->_left != nullptr)
			{
				SubRight = SubRight->_right;
			}
			_node = SubRight;
		}
		//it指向当前结点,若左子树为空,寻找祖先结点中某个结点是其父亲的右子树的结点
		else
		{
			Node* parent = _node->_parent;
			Node* cur = _node;
			while (parent&&parent->_left == cur)
			{
				cur = parent;
				parent = cur->_parent;
			}
			_node = parent;
		}
		return *this;
	}
	bool operator!=(const Self& s)
	{
		return _node != s._node;
	}

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

map的模拟实现

operator [ ]

operator[]通过键值k来访问map容器中对应的值value;

如果键值k不存在,则会自动插入一个新的键值对,其中键值为k,value值为默认构造的值

如果键值k已经存在,则会返回与该键相关联的value值;

//operator[]等价形式如下:
(*((this->insert(make_pair(k,mapped_type()))).first)).second

首先this指针调用insert()函数即this->insert(make_pair(k,mapped_type())),插入的键值对中的键值key为k,value值为mapped_type类型的默认构造函数,而insert()函数的返回值为结构体pair<iterator, bool>,结构体pair中的第一个元素为迭代器(迭代器本质为指针或者封装过的指针),当取到迭代器位置first对其解引用便可获得map容器中的与k相对应的value值;

由于map中的operator[ ]返回的类型为键值对pair<iterator bool>, 而红黑树底层的insert()函数返回bool值,因此需要对红黑树底层的insert()修改;

  • 键值对<iterator,bool>: iterator代表新插入结点的迭代器,bool值表示插入是否成功;
  • 若新结点键值key原先存在,则返回原本存在结点的迭代器,并且插入失败,返回false;
  • 若新结点键值key原先不存在,则插入结点,返回新插入结点在红黑树中的迭代器,返回true;
template<class K,class V>
class map
{
	//仿函数
	struct MapKeyOfT//获取map容器中数据的键值key
	{
		const K& operator()(const pair<K,V>& kv)
		{
			return kv.first;
		}
	};
public:
	typedef typename RBTree<K, pair<K, V>, MapKeyOfT>::iterator iterator;

	iterator begin()
	{
		return _t.begin();
	}
	iterator end()
	{
		return _t.end();
	}
	pair<iterator,bool> insert(const pair<K, V>& kv)
	{
		return _t.insert(kv);
	}
	V& operator[](const K& key)
	{
		pair<iterator, bool> ret = insert(make_pair(key, V()));
		return ret.first->second;
	}
	iterator Find(const K& key)
	{
		return _t.Find();
	}
private:
	RBTree<K, pair<K, V>, MapKeyOfT> _t;
};

set的模拟实现

template<class K>
class set
{
	//仿函数
	struct SetKeyOfT//获取set容器中数据的键值key
	{
		const K& operator()(const K& key)
		{
			return key;
		}
	};
public:
	typedef typename RBTree<K, K, SetKeyOfT>::iterator iterator;

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

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

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

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

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

红黑树底层结构

// 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);
	}
	pair<iterator,bool> insert(const T& data)
	{
		if (_root == nullptr)
		{
			_root = new Node(data);
			_root->_col = BLACK;
			return make_pair(iterator(_root), true);
		}
		Node* parent = nullptr;
		Node* cur = _root;
		KeyOfT kot;
		while (cur != nullptr)
		{
			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), false);
			}
		}
		//开辟结点,存储数据,建立链接关系
		cur = new Node(data);
		Node* newnode = cur;
		if (kot(parent->_data) > kot(data))
		{
			parent->_left = cur;
		}
		else
		{
			parent->_right = cur;
		}
		cur->_parent = parent;

		//平衡化操作
		while (parent && parent->_col == RED)
		{
			Node* grandfather = parent->_parent;
			if (parent == grandfather->_left)
			{
				Node* uncle = grandfather->_right;
				// 情况一:叔叔存在且为红
				if (uncle && uncle->_col == RED)
				{
					// 变色
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					// 继续往上处理
					cur = grandfather;
					parent = cur->_parent;
				}
				else
				{
					if (cur == parent->_left)
					{
						RotateR(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
						RotateL(parent);
						RotateR(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}

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

					// 继续往上处理
					cur = grandfather;
					parent = cur->_parent;
				}
				else
				{
					if (cur == parent->_right)
					{
						RotateL(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
						RotateR(parent);
						RotateL(grandfather);
						cur->_col = BLACK;
						grandfather->_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;

		subR->_left = parent;
		Node* ppnode = parent->_parent;
		parent->_parent = subR;

		if (parent == _root)
		{
			_root = subR;
			subR->_parent = nullptr;
		}
		else
		{
			if (ppnode->_left == parent)
			{
				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;

		subL->_right = parent;

		Node* ppnode = parent->_parent;
		parent->_parent = subL;

		if (parent == _root)
		{
			_root = subL;
			subL->_parent = nullptr;
		}
		else
		{
			if (ppnode->_left == parent)
			{
				ppnode->_left = subL;
			}
			else
			{
				ppnode->_right = subL;
			}
			subL->_parent = ppnode;
		}
	}
	//红黑树的检测
	void _InOrder(Node* root)
	{
		if (root == nullptr)
			return;

		_InOrder(root->_left);
		cout << root->_kv.first << endl;
		_InOrder(root->_right);
	}

	void InOrder()
	{
		_InOrder(_root);
	}
	bool Check(Node* cur)
	{
		if (cur == nullptr)
			return true;

		//检查红黑树性质二
		if (cur->_col == RED && cur->_parent->_col == RED)
		{
			cout << cur->_kv.first << "存在连续的红色节点" << endl;
			return false;
		}

		return Check(cur->_left) && Check(cur->_right);
	}

	iterator* Find(const T& data)
	{
		KeyOfT kot;//定义仿函数对象
		Node* cur = _root;
		while (cur)
		{
			if (kot(cur->_data) < kot(data))
			{
				cur = cur->_right;
			}
			else if (kot(cur->_data)  > kot(data))
			{
				cur = cur->_left;
			}
			else
			{
				return cur;
			}
		}
		return nullptr;
	}

	bool Check(Node* cur, int blackNum, int refBlackNum)
	{
		if (cur == nullptr)
		{
			if (refBlackNum != blackNum)
			{
				cout << "黑色节点的数量不相等" << endl;
				return false;
			}

			return true;
		}

		if (cur->_col == RED && cur->_parent->_col == RED)
		{
			cout << cur->_kv.first << "存在连续的红色节点" << endl;
			return false;
		}

		if (cur->_col == BLACK)
			++blackNum;

		return Check(cur->_left, blackNum, refBlackNum)
			&& Check(cur->_right, blackNum, refBlackNum);
	}
	bool IsRBTree()
	{
		//检查根结点是否为黑色
		if (_root && _root->_col == RED)
			return false;

		int refBlackNum = 0;//基准值
		Node* cur = _root;
		while (cur)
		{
			if (cur->_col == BLACK)
				refBlackNum++;

			cur = cur->_left;
		}

		return Check(_root, 0, refBlackNum);
	}
private:
	Node* _root = nullptr;
};

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小呆瓜历险记

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

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

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

打赏作者

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

抵扣说明:

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

余额充值