【ONE·C++ || set和map(三):基于红黑树的封装框架】

文章详细介绍了如何基于红黑树实现自定义的map和set容器,包括利用仿函数解决不同数据类型的比较问题以及实现迭代器,包括begin(),end(),operator*等操作,并提供了插入操作满足pair<iterator,bool>返回值的方法。
摘要由CSDN通过智能技术生成

总言

  主要介绍map、set的封装框架。


  
  
  

1、基本框架说明

  1)、问题说明与stl底层查看
  问题:map和set底层都是通过红黑树实现,但我们知道二者功能各不相同,比如map为Key-Value模型,而set为Key模型。如果通过红黑树实现,是否意味着我们需要分别实现两个功能类似的红黑树?
  
  我们来看看库里是如何实现的:


#ifndef __SGI_STL_SET_H
#define __SGI_STL_SET_H

#include <tree.h>
#include <stl_set.h>

#ifdef __STL_USE_NAMESPACES
using __STD::set;
#endif /* __STL_USE_NAMESPACES */

#endif /* __SGI_STL_SET_H */
#ifndef __SGI_STL_MAP_H
#define __SGI_STL_MAP_H

#include <tree.h>
#include <stl_map.h>

#ifdef __STL_USE_NAMESPACES
using __STD::map;
#endif /* __STL_USE_NAMESPACES */

#endif /* __SGI_STL_MAP_H */

  
  相关分析:
在这里插入图片描述
  
  
  
  2)、map、set中框架搭建
  根据上述分析,我们来对原先的红黑树做修改,相关框架:set和map(二):AVL树和红黑树

在这里插入图片描述

template<class T>
struct RBTreeNode//红黑树结点
{
	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right;
	RBTreeNode<T>* _parent;

	T _data;//当前结点存储值
	Colour _col;

	RBTreeNode(const T& data)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_data(data)
		,_col(RED)
	{ }

};

//此处我们实现红黑树结点只需要第二参数T,那么第一参数K有何用?
//回答:比如find,我们要根据第一参数Key的值,找到对应的结点。
template<class K,class T>
struct RBTree //红黑树
{
	typedef RBTreeNode<T> Node;
	//……
private:
	Node* _root = nullptr;
};
namespace my_set
{
	template<class K>
	class set
	{
	public:
		//……
		//其它实现
		
	private:
		RBTree<K, K> _t;
	};
}
namespace my_map
{
	template<class K, class V>
	class map
	{
	public:
		//……
		//其它实现
		
	private:
		RBTree<K, pair<K, V>> _t;
	};
}

  
  
  
  
  
  

2、map、set封装Ⅰ:用于比较的仿函数

  1)、问题引入
  上述修改后有一个问题: 节点的比较。在原先红黑树中,我们实现了Insert,其判断结点位置时,直接通过运算符进行比较查询合适结点位置。

			if (cur->_data.first < data.first)
			{
				//……
			}
			else if (cur->_data.first > data.first)
			{
				//……
			}

  但这里RBTree是用于实现set、map。对于T _data;,二者一个结点类型为K,一个为pair<K,V>,但都要同时做到使用K(Key)值比较判断。如何做到?
  
  
  
  2)、相关实现
  方法说明:这里我们为set、map分别实现一个仿函数,根据其各自需求,返回我们需要的用于比较的值:

在这里插入图片描述

  
  在set中:

	template<class K>
	class set
	{
	public:
		struct SetKeyofT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};

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

  在map中:

	template<class K, class V>
	class map
	{
	public:
		struct MapKeyofT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};

	private:
		RBTree<K, pair<K, V>, MapKeyofT> _t;
	};

  RBTree中,相关查找修改如下:

			KeyOfT kot;
			
			if (kot(cur->_data) < kot(data))//若当前结点值比待插入结点值小,说明待插入结点值在其右子树
			{
				//……
			}
			else if (kot(cur->_data) > kot(data))
			{
				//……
			}

  框架展示:我们增添了一个class KeyOfT,根据map、set传入的不同仿函数,获取不同结果。

template<class K, class T, class KeyOfT>
struct RBTree
{
	typedef RBTreeNode<T> Node;

public:
	bool Insert(const T& data)
	{
		KeyOfT kot;

		//step1:按照二叉搜索树的方式插入新节点
		Ⅰ、寻找位置
		if (_root == nullptr)//单独处理:根节点为空时
		{
			_root = new Node(data);
			_root->_col = BLACK;//根节点默认为黑色
			return true;
		}

		//根节点不为空:
		Node* cur = _root;
		Node* parent = nullptr;
		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//key值已存在
			{
				return false;
			}
		}

		Ⅱ、插入值,调整链接关系
		cur = new Node(data);
		cur->_parent = parent;
		if (kot(parent->_data) < kot(data))//cur插入在parent的右孩子处
			parent->_right = cur;
		else
			parent->_left = cur;//cur插入在parent的左孩子处
		cur->_col = RED;//将插入的结点设置为红色

		//step2:检查新增结点后,是否还满足红黑树性质
		//……


	}
	
	Node* _root = nullptr;
};

  
  
  
  
  
  

3、map、set封装Ⅱ:迭代器实现

3.1、基本说明

  1)、基本说明
  通常情况下,begin()end()代表的是一段前闭后开的区间。反映到红黑树中,begin()表示中序遍历下第一个结点(即最左侧结点/最小结点);end()表示最后一个结点的下一个位置(即最右侧结点/最大结点)。

  一种实现方案:通常情况下会给出一个header头结点,类似于链表中的哨兵位头,其left、right分别指向最左结点和最右结点,即我们需要的begin()、end()
在这里插入图片描述

  下述实现中我们并没有借鉴哨兵位的头。
  
  
  2)、迭代器框架
  说明:set、map的迭代器,实则使用的是红黑树的迭代器,因此实现时,我们需要先实现红黑树中的迭代器,对set、map而言,则通过封装达成相关需求。
  
  红黑树中,迭代器基本框架如下:类比于List迭代器实现。

template<class T, class Ref, class Ptr>
struct __RBTreeIterator
{
	typedef RBTreeNode<T> Node;
	typedef __RBTreeIterator<T, Ref, Ptr> iterator;

	Node* _node;//成员变量:指向结点的指针

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

	//实现++、--、*、&等
	//……

};

  
  
  
  

3.2、begin()、end()、operator*、operator&、operator==、operator!=

3.2.1、begin()、end()

  1)、在红黑树中

template<class K, class T, class KeyOfT>
struct RBTree
{
	typedef RBTreeNode<T> Node;
	typedef __RBTreeIterator<T, T&, T*> iterator;
	typedef __RBTreeIterator<T, const T&, const T*> const_iterator;

public:

	iterator begin()
	{	//begin()在红黑树中取最左结点
		Node* cur = _root;
		while (cur && cur->_left)
		{
			cur = cur->_left;
		}
		return iterator(cur);//匿名构造
	}

	iterator end()
	{
		return iterator(nullptr);
	}
	
	//……

}

  
  2)、在map、set中
  map、set中迭代器只需要调用红黑树的迭代器即可。注意此处typename的使用说明。

		typedef typename RBTree<K, K, SetKeyofT>::iterator iterator;
		iterator begin()
		{
			return _t.begin();
		}

		iterator end()
		{
			return _t.end();
		}
		typedef typename RBTree<K, pair<K, V>, MapKeyofT>::iterator iterator;
		iterator begin()
		{
			return _t.begin();
		}

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

  
  
  

3.2.2、operator*、operator&、operator==、operator!=

  在红黑树的迭代器中:(struct __RBTreeIterator)

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

	Ptr operator&()
	{
		return &(_node->_data);
	}
	bool operator==(const iterator& it)const
	{
		return _node == it._node;
	}

	bool operator!=(const iterator& it)const
	{
		return _node != it._node;
	}

  
  
  
  

3.3、operator++、operator- -

3.3.1、operator++

  红黑树中,operator++要满足中序遍历,访问当前结点的下一个位置,此时会存在两种情况:
在这里插入图片描述  说明:关于此处迭代器的++--,关键需要理解为什么要这样做。本质是因为在此过程中,对左子树、根节点、右子树,根节点会被访问到两次,需要将其跳过。
  
  
  2)、相关实现

	iterator& operator++()
	{
		if (_node->_right)//当前节点右子树不为空:访问其右子树最左节点
		{
			Node* cur = _node->_right;
			while (cur->_left)
				cur = cur->_left;
			_node = cur;//让迭代器来到当前结点处
		}
		else//当前节点右子树为空:沿祖先链迭代,寻找首个孩子非右孩子的父节点
		{
			Node* cur = _node;
			Node* parent = _node->_parent;
			while (parent && cur == parent->_right)
			{
				cur = parent;
				parent = cur->_parent;
			}
			_node = parent;//此处迭代器来到的是parent位置处
		}

		return *this;//最后返回迭代器本身
	}

  
  

3.3.2、operator- -

  反方向进行即可。

	iterator operator--()
	{
		if (_node->_left)//若当前节点左子树存在
		{
			Node* cur = _node->_left;//--的下一个节点是该左子树的最右值
			while (cur->_right)
				cur = cur->_right;
			_node = cur;
		}
		else//当前节点左子树不存在:说明--依次访问了右子树、当前节点、左子树,要返回当前节点的上一层
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_left)
			{
				cur = parent;
				parent = cur->_parent;
			}
			_node = parent;
		}
		return *this;
	}

  
  
  
  
  
  
  

3.4、Insert满足pair<iterator,bool>返回值、operator[]

3.4.1、Insert

  在之前学习map、set时,对insert的返回值我们有简单介绍:
在这里插入图片描述
  这里我们同样做出修改:

	pair<iterator,bool> Insert(const T& data)
	{
		KeyOfT kot;//用于辅佐完成set和map因类型不同而无法统一判断

		//step1:按照二叉搜索树的方式插入新节点
		Ⅰ、寻找位置
		if (_root == nullptr)
		{
			_root = new Node(data);
			_root->_col = BLACK;
			return make_pair(iterator(_root), true);//返回新增节点处的迭代器及bool值
		}

		//根节点不为空:
		Node* cur = _root;
		Node* parent = nullptr;
		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),false);//key值已存在:返回原先存在的节点的迭代器
			}
		}

		Ⅱ、插入值,调整链接关系
		cur = new Node(data);
		Node* newnode = cur;//用于返回
		cur->_parent = parent;
		if (kot(parent->_data) < kot(data))
			parent->_right = cur;
		else
			parent->_left = cur;
		cur->_col = RED;

		//step2:检查新增结点后,是否还满足红黑树性质
		//此处省略
		
		_root->_col = BLACK;
		return make_pair(iterator(newnode), true);//返回新增节点处的迭代器及bool值

	}

  
  
  

3.4.2、operator[]

  有了上述对于insert的改造,map中operator[]就能够实现(此处的相关解释见前文:set和map(一))

		V& operator[](const K& key)
		{
			pair<iterator, bool> ret = insert(make_pair(key, V()));
			return ret.first->second;//rer.first获取iterator,iterator调用opeator->
		}

  
  测试代码如下:


#include"map.h"
void testmap()
{
	//次数统计
	string arr[] = { "晴","多云","晴","阴","小雨","多云","多云","阴","晴","小雨","大雨","阴","多云","晴" };
	my_map::map<string, int> countMap;
	for (auto& str : arr)//直接借助范围for遍历
	{
		countMap[str]++;
	}

	//范围for
	for (const auto& kv : countMap)
	{
		cout << kv.first << ": " << kv.second << endl;
	}

}

在这里插入图片描述

  
  
  

4、总览

4.1、set

#pragma once

#include"RedBlackTree.h"


namespace my_set
{
	template<class K>
	class set
	{
		struct SetKeyofT
		{
			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& val)
		{
			return _t.Insert(val);
		}

		

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

  
  
  

4.2、map

#pragma once
#include"RedBlackTree.h"


namespace my_map
{
	template<class K, class V>
	class map
	{
	public:
		struct MapKeyofT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};

		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>& val)
		{
			return _t.Insert(val);
		}

		V& operator[](const K& key)
		{
			pair<iterator, bool> ret = insert(make_pair(key, V()));
			return ret.first->second;//rer.first获取iterator,iterator调用opeator->
		}
	private:
		RBTree<K, pair<K, V>, MapKeyofT> _t;
	};
}

  
  
  

4.3、RedBlackTree

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



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

};

template<class T, class Ref, class Ptr>
struct __RBTreeIterator
{
	typedef RBTreeNode<T> Node;
	typedef __RBTreeIterator<T, Ref, Ptr> iterator;

	Node* _node;//成员变量:指向结点的指针

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

	//实现++、--、*、->
	Ref operator* ()
	{
		return _node->_data;
	}

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

	bool operator==(const iterator& it)const
	{
		return _node == it._node;
	}

	bool operator!=(const iterator& it)const
	{
		return _node != it._node;
	}

	iterator& operator++()
	{
		if (_node->_right)//当前节点右子树不为空:访问其右子树最左节点
		{
			Node* cur = _node->_right;
			while (cur->_left)
				cur = cur->_left;
			_node = cur;//让迭代器来到当前结点处
		}
		else//当前节点右子树为空:沿祖先链迭代,寻找首个孩子非右孩子的父节点
		{
			Node* cur = _node;
			Node* parent = _node->_parent;
			while (parent && cur == parent->_right)
			{
				cur = parent;
				parent = cur->_parent;
			}
			_node = parent;//此处迭代器来到的是parent位置处
		}

		return *this;//最后返回迭代器本身
	}


	iterator operator--()
	{
		if (_node->_left)//若当前节点左子树存在
		{
			Node* cur = _node->_left;//--的下一个节点是该左子树的最右值
			while (cur->_right)
				cur = cur->_right;
			_node = cur;
		}
		else//当前节点左子树不存在:说明--依次访问了右子树、当前节点、左子树,要返回当前节点的上一层
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_left)
			{
				cur = parent;
				parent = cur->_parent;
			}
			_node = parent;
		}
		return *this;
	}

};

template<class K, class T, class KeyOfT>
struct RBTree
{
	typedef RBTreeNode<T> Node;
	typedef __RBTreeIterator<T, T&, T*> iterator;
	typedef __RBTreeIterator<T, const T&, const T*> const_iterator;

public:

	iterator begin()
	{	//begin()在红黑树中取最左结点
		Node* cur = _root;
		while (cur && cur->_left)
		{
			cur = cur->_left;
		}
		return iterator(cur);//匿名构造
	}

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

	pair<iterator,bool> Insert(const T& data)
	{
		KeyOfT kot;

		//step1:按照二叉搜索树的方式插入新节点
		Ⅰ、寻找位置
		if (_root == nullptr)//单独处理:根节点为空时
		{
			_root = new Node(data);
			_root->_col = BLACK;//根节点默认为黑色
			return make_pair(iterator(_root), true);
		}

		//根节点不为空:
		Node* cur = _root;
		Node* parent = nullptr;
		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//key值已存在
			{
				return make_pair(iterator(cur),false);
			}
		}

		Ⅱ、插入值,调整链接关系
		cur = new Node(data);
		Node* newnode = cur;//用于返回
		cur->_parent = parent;
		if (kot(parent->_data) < kot(data))//cur插入在parent的右孩子处
			parent->_right = cur;
		else
			parent->_left = cur;//cur插入在parent的左孩子处
		cur->_col = RED;//将插入的结点设置为红色

		//step2:检查新增结点后,是否还满足红黑树性质
		while (parent && parent->_col == RED)//新结点后,无论是cur本身,还是迭代后变色,如果parent、cur颜色皆红,则违反性质3:不能有连续的红结点,因此需要调整
		{
			//固定:
			Node* grandparent = parent->_parent;
			assert(grandparent && grandparent->_col == BLACK);//grandparent存在且为黑

			//关键看叔叔,此处对uncle和parent在不同位置分别处理
			if (parent == grandparent->_left)
			{
				Node* uncle = grandparent->_right;
				
				//情况一
				if (uncle&& uncle->_col == RED)//叔叔存在且为红
				{
					//处理方法:变色
					parent->_col = uncle->_col = BLACK;
					grandparent->_col = RED;
					//继续向上调整
					cur = grandparent;
					parent = cur->_parent;
				}
				else //叔叔不存在或叔叔存在且为黑:情况二+情况三
				{
					//处理方法:旋转+变色(旋转有四类)

					//Ⅰ:情况二:p在g左,c在p左,成直线。右单旋+g变色为红,p变色为黑
					if (cur == parent->_left)
					{
						RotateR(grandparent);
						grandparent->_col = RED;
						parent->_col = BLACK;
					}
					else//Ⅱ:情况三:p在g左,c在p右,成折线。左右双旋+g变色为红,c变色为黑
					{
						RotateL(parent);
						RotateR(grandparent);
						grandparent->_col = RED;
						cur->_col = BLACK;
					}
					break;//情况二、情况三完成调整后,满足红黑树规则,不必继续向上调整
				}
			}
			else//parent == grandparent->_right
			{
				Node* uncle = grandparent->_left;

				//情况一
				if (uncle&& uncle->_col == RED)//叔叔存在且为红
				{
					//处理方法:变色
					parent->_col = uncle->_col = BLACK;
					grandparent->_col = RED;
					//继续向上调整
					cur = grandparent;
					parent = cur->_parent;
				}
				else //叔叔不存在或叔叔存在且为黑:情况二+情况三
				{
					//处理方法:旋转+变色(旋转有四类)

					//Ⅰ:情况二:p在g右,c在p右,成直线。左单旋+g变色为红,p变色为黑
					if (cur == parent->_right)
					{
						RotateL(grandparent);
						grandparent->_col = RED;
						parent->_col = BLACK;
					}
					else//Ⅱ:情况三:p在g右,c在p左,成折线。右左双旋+g变色为红,c变色为黑
					{
						RotateR(parent);
						RotateL(grandparent);
						grandparent->_col = RED;
						cur->_col = BLACK;
					}
					break;//情况二、情况三完成调整后,满足红黑树规则,不必继续向上调整
				}
			}

		}
		_root->_col = BLACK;
		return make_pair(iterator(newnode), true);

	}


	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}

	bool IsBalance()
	{
		if (_root == nullptr)
			return true;
		if (_root->_col == RED)
		{
			cout << "根节点不满足黑色" << endl;
			return false;
		}

		int BasicalNum = 0;//用于确定基准值:前序遍历时的第一条路径
		int blackNum = 0;//用于统计树中黑色结点数
		return PrevCheck(_root, blackNum, BasicalNum);
	}

private:
	bool PrevCheck(Node* root, int blackNum, int& basicalNum)
	{
		if (root == nullptr)
		{
			if (basicalNum == 0)
			{
				basicalNum == blackNum;
				return true;
			}
			else
			{
				if (blackNum == basicalNum)
					return true;
				else
				{
					cout << "某条路径黑色结点数不匹配" << endl;
					return false;
				}
			}
		}

		if (root->_col == BLACK)
			++blackNum;
		if (root->_col == RED && root->_parent->_col == RED)
		{
			cout << "存在连续的红色结点" << endl;
			return false;
		}

		return PrevCheck(root->_left, blackNum, basicalNum)
			&& PrevCheck(root->_right, blackNum, basicalNum);
	}

	void _InOrder(Node* root)
	{
		if (root == nullptr)
			return;
		_InOrder(root->_left);
		//cout << root->_kv.first << "," << root->_kv.second << endl;
		_InOrder(root->_right);
	}

	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRtoL = subR->_left;
		Node* grendparent = parent->_parent;

		//修改链接关系以达成旋转
		parent->_right = subRtoL;
		parent->_parent = subR;

		if (subRtoL)//h>=0,subRtoL可能不存在
			subRtoL->_parent = parent;

		subR->_left = parent;
		if (_root == parent)//parent为原先AVL树的根节点
		{
			subR->_parent = nullptr;
			_root = subR;
		}
		else//parent为原先AVL树的分支节点
		{
			subR->_parent = grendparent;
			if (grendparent->_left == parent)
				grendparent->_left = subR;
			else
				grendparent->_right = subR;
		}
	}


	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLtoR = subL->_right;
		Node* grendparent = parent->_parent;

		//处理链接关系
		parent->_left = subLtoR;
		parent->_parent = subL;

		if (subLtoR)
			subLtoR->_parent = parent;

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

	Node* _root = nullptr;
};

  
  
  
  
  
  
  
  
  
  
  
  
  

Fin、共勉。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值