C++实现红黑树(RBTree) + 模拟实现map set

9 篇文章 0 订阅

目录

 一、红黑树(RBTree)

1.1 红黑树概念与性质

1.2 红黑树节点的定义

1.3 红黑树模拟实现

1.3.1 红黑树成员框架

1.3.2 红黑树调整情形

1.3.3 insert() 插入结点

1.3.4 IsBalanceTree() 判断是否为平衡搜索树

二、关联式容器与键值对

2.1 关联式容器概念

2.2 键值对

 三、树形结构的关联式容器

3.1 set 

3.1.1 set的介绍

3.1.2 set的函数使用

3.2 map

3.2.1 map的介绍

3.2.2 map的函数接口介绍与使用

3.3.3 multiset multimap

四、模拟实现set 和 map

4.1 底层红黑树

4.1.1 红黑树类模板参数

4.1.2 红黑树迭代器实现

4.1.3 红黑树完整版

4.2 set的模拟实现 

 4.3 模拟实现map

4.4 测试set和map

4.4.1 set 测试

4.4.2 map测试 


 一、红黑树(RBTree)

1.1 红黑树概念与性质

红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。

性质:

1. 每个结点不是红色就是黑色
2. 根节点是黑色的
3. 如果一个节点是红色的,则它的两个孩子结点是黑色的
4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点
5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)

如何理解红黑树最长路径长度不会超过最短路径的两倍?

结合性质4,最长路径呈一红一黑的样式向下,而最短路径是全黑向下!比如我们现在每条路径黑色节点树是3,那么最坏的那一条路径是黑 红 黑 红 黑 最短的是黑 黑 黑 前者要遍历5个节点,后者要遍历3个节点,通过数学分析我们也能得到这个结果,这里就不证明了~


1.2 红黑树节点的定义

//红黑树结点颜色
enum colour
{
	RED,
	BLACK
};

template <class ValType>
//结点类
struct TreeNode
{
public:
	TreeNode(const ValType& val)
		:_val(val)
		,_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_colour(RED)
	{}
	ValType _val;
	TreeNode<ValType>* _left;
	TreeNode<ValType>* _right;
	TreeNode<ValType>* _parent;
	colour _colour;
};

插入的节点颜色为什么初始为红色?

我们通过红黑树的规则,后面用旋转的方式来解决不满足规则的情况,性质3是红色节点的约束条件,我们插入黑色节点很难判断是否打破规则,而红色节点的连续出现会告诉我们规则被打破了,要调整树!


1.3 红黑树模拟实现

1.3.1 红黑树成员框架

template <class ValType>
class RBTree
{
public:
	typedef TreeNode<ValType> Node;
	RBTree()
		:_root(nullptr)
	{}
	
	void Inorder()
	{
		_Inorder(_root);
	}
	
private:
	void _Inorder(const Node* root)
	{
		if (root == nullptr)
			return;
		_Inorder(root->_left);
		//cout << (root->_val).second << ' ';
		cout <<"[" << (root->_val).first << "," << (root->_val).second <<"]" << endl;
		_Inorder(root->_right);
	}
	Node* _root;
};

1.3.2 红黑树调整情形

约定:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点

情况一:cur为红,p为红,g为黑,u存在且为红

解决方式:将p,u改为黑,g改为红,然后把g当成cur,继续向上调整。


情况二:cur为红,p为红,g为黑,u不存在/u存在且为黑

解决方式:

p为g的左孩子,cur为p的左孩子,则进行右单旋转;相反,
p为g的右孩子,cur为p的右孩子,则进行左单旋转
p、g变色--p变黑,g变红


情况三:cur为红,p为红,g为黑,u不存在/u存在且为黑

解决方式:

p为g的左孩子,cur为p的右孩子,则针对p做左单旋转;相反,
p为g的右孩子,cur为p的左孩子,则针对p做右单旋转
则转换成了情况2


1.3.3 insert() 插入结点

    void RotateR(Node* parent)
 	{
		Node* SubL = parent->_left;
		Node* SubLR = SubL->_right;
		parent->_left = SubLR;
		if (SubLR) SubLR->_parent = parent;
		Node* gparent = parent->_parent;
		if (parent == _root)
		{
			_root = SubL;
			SubL->_parent = nullptr;
		}
		else {
			if (parent == gparent->_left)
				gparent->_left = SubL;
			else gparent->_right = SubL;
			SubL->_parent = parent->_parent;
		}
		SubL->_right = parent;
		parent->_parent = SubL;
	}
	void RotateL(Node* parent)
	{
		Node* SubR = parent->_right;
		Node* SubRL = SubR->_left;
		parent->_right = SubRL;
		if (SubRL) SubRL->_parent = parent;
		Node* gparent = parent->_parent;
		if (parent == _root)
		{
			_root = SubR;
			SubR->_parent = nullptr;
		}
		else {
			if (parent == gparent->_left)
				gparent->_left = SubR;
			else gparent->_right = SubR;
			SubR->_parent = gparent;
		}
		SubR->_left = parent;
		parent->_parent = SubR;
	}

bool insert(const ValType& val)
	{
		
		if (_root == nullptr)
		{
			_root = new Node(val);
			_root->_colour = BLACK;
			return make_pair(iterator(_root), true);
		}
		Node* cur = _root;
		Node* parent = nullptr;
		//查找插入点
		while (cur)
		{
			if (cur->val < val)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_val > val)
			{
				parent = cur;
				cur = cur->_left;
			}
			else return false;
		}
		//链接关系
		cur = new Node(val);
		Node* newnode = cur;
		cur->_parent = parent;
		if (cur->_val > parent->_val)
			parent->_right = cur;
		else parent->_left = cur;
		//插入后判断是否满足红黑树要求
		while (parent && parent->_colour == RED)
		{
			Node* gparent = parent->_parent;
			Node* uncle = nullptr;
			if (parent == gparent->_left)
				uncle = gparent->_right;
			else uncle = gparent->_left;
			//情况一
			if (uncle && uncle->_colour == RED)
			{
				parent->_colour = uncle->_colour = BLACK;
				gparent->_colour = RED;
				cur = gparent;
				parent = cur->_parent;
			}
			//情况二 + 情况三
			else {
				if (uncle == gparent->_right)
				{
					//情况二
					if (cur == parent->_left)
					{
						RotateR(gparent);
						gparent->_colour = RED;
						parent->_colour = BLACK;
					}
					//情况三 
					else {
						RotateL(parent);
						RotateR(gparent);
						gparent->_colour = RED;
						cur->_colour = BLACK;
					}
				}
				else {
					//情况二
					if (cur == parent->_right)
					{
						RotateL(gparent);
						gparent->_colour = RED;
						parent->_colour = BLACK;
					}
					//情况三
					else {
						RotateR(parent);
						RotateL(gparent);
						gparent->_colour = RED;
						cur->_colour = BLACK;
					}
				}
			}
		}
		//插入过程可能改变根的颜色,根必须为黑!!!
		_root->_colour = BLACK;
		return true;

1.3.4 IsBalanceTree() 判断是否为平衡搜索树

bool check(Node* root,int BlackNodeNum,int ref)
	{
		//走到空,判断此时黑节点累计数量是否满足标准
		if (root == nullptr)
		{
			if (BlackNodeNum != ref)
				return false;
			return true;
		}
		//连续出现两次红色结点,不满足规则
		if (root->_colour == RED && root->_parent->_colour == RED)
			return false;
		//每次遇到黑节点 ++黑节点个数
		if (root->_colour == BLACK)
		{
			BlackNodeNum++;
		}
		//递归左右子树
		return check(root->_left, BlackNodeNum, ref) && check(root->_right, BlackNodeNum, ref);
	}
	bool IsBalanceTree()
	{
		if (_root == nullptr)
			return true;
		if (_root->_colour == RED)
		{
			return false;
		}
		Node* left = _root;
		//计算一条路径的黑色结点个数,以它为标准审视每条路径
		int ref = 0;
		int BlackNodeNum = 0;
		while (left)
		{
			if (left->_colour == BLACK)
				++ref;
			left = left->_left;
		}
		return check(_root, BlackNodeNum, ref);
	}

二、关联式容器与键值对

2.1 关联式容器概念

在初阶阶段,我们已经接触过STL中的部分容器,比如:vector、list、deque、
forward_list(C++11)等,这些容器统称为序列式容器,因为其底层为线性序列的数据结构,里面存储的是元素本身。那什么是关联式容器?它与序列式容器有什么区别?
关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是<key, value>结构的键值对在数据检索时比序列式容器效率更高。

2.2 键值对

用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代表键值,value表示与key对应的信息。比如:现在要建立一个英汉互译的字典,那该字典中必然有英文单词与其对应的中文含义,而且,英文单词与其中文含义是一一对应的关系,即通过该应该单词,在词典中就可以找到与其对应的中文含义

pair - C++ Reference (cplusplus.com)

//STL对于键值对的定义

template <class T1, class T2>
struct pair
{
	typedef T1 first_type;
	typedef T2 second_type;
	T1 first;
	T2 second;
	pair() : first(T1()), second(T2())
	{}
	pair(const T1& a, const T2& b) : first(a), second(b)
	{}
};

 三、树形结构的关联式容器

根据应用场景的不桶,STL总共实现了两种不同结构的管理式容器:树型结构与哈希结构。树型结构的关联式容器主要有四种:map、set、multimap、multiset。这四种容器的共同点是:使用平衡搜索树(即红黑树)作为其底层结果,容器中的元素是一个有序的序列。下面一依次介绍每一个容器。

3.1 set 

3.1.1 set的介绍

1. set是按照一定次序存储元素的容器


2. 在set中,元素的value也标识它(value就是key,类型为T),并且每个value必须是唯一的。set中的元素不能在容器中修改(元素总是const),但是可以从容器中插入或删除它们


3. 在内部,set中的元素总是按照其内部比较对象(类型比较)所指示的特定严格弱排序准则进行排序。


4. set容器通过key访问单个元素的速度通常比unordered_set容器慢,但它们允许根据顺序对
子集进行直接迭代。


5. set在底层是用二叉搜索树(红黑树)实现的

注意:

1. 与map/multimap不同,map/multimap中存储的是真正的键值对<key, value>,set中只放value,但在底层实际存放的是由<value, value>构成的键值对。
2. set中插入元素时,只需要插入value即可,不需要构造键值对。
3. set中的元素不可以重复(因此可以使用set进行去重)。
4. 使用set的迭代器遍历set中的元素,可以得到有序序列
5. set中的元素默认按照小于来比较
6. set中查找某个元素,时间复杂度为:log_2 n
7. set中的元素不允许修改
8. set中的底层使用二叉搜索树(红黑树)来实现。

3.1.2 set的函数使用

set函数操作汇总链接:set - C++ Reference (cplusplus.com)

#include <set>
void TestSet()
{
	// 用数组array中的元素构造set
	int array[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0, 1, 3, 5, 7, 9, 2, 4,
	6, 8, 0 };
	set<int> s(array, array + sizeof(array) / sizeof(array));
	cout << s.size() << endl;
	// 正向打印set中的元素,从打印结果中可以看出:set可去重
	for (auto& e : s)
		cout << e << " ";
	cout << endl;
	// 使用迭代器逆向打印set中的元素
	for (auto it = s.rbegin(); it != s.rend(); ++it)
		cout << *it << " ";
	cout << endl;
	// set中值为3的元素出现了几次
	cout << s.count(3) << endl;
}


3.2 map

3.2.1 map的介绍

1. map是关联容器,它按照特定的次序(按照key来比较)存储由键值key和值value组合而成的元素。


2. 在map中,键值key通常用于排序和惟一地标识元素,而值value中存储与此键值key关联的
内容。键值key和值value的类型可能不同,并且在map的内部,key与value通过成员类型
value_type绑定在一起
,为其取别名称为pair:typedef pair<const key, T> value_type;


3. 在内部,map中的元素总是按照键值key进行比较排序的


4. map中通过键值访问单个元素的速度通常比unordered_map容器慢,但map允许根据顺序
对元素进行直接迭代(即对map中的元素进行迭代时,可以得到一个有序的序列)。


5. map支持下标访问符,即在[]中放入key,就可以找到与key对应的value。


6. map通常被实现为二叉搜索树(更准确的说:平衡二叉搜索树(红黑树))。


3.2.2 map的函数接口介绍与使用

map函数操作汇总:map - C++ Reference (cplusplus.com)

注意:在元素访问时,有一个与operator[]类似的操作at()(该函数不常用)函数,都是通过
key找到与key对应的value然后返回其引用,不同的是:当key不存在时,operator[]用默认
value与key构造键值对然后插入,返回该默认value,at()函数直接抛异常!

void TestMap()
{
	map<string, string> m;
	// 向map中插入元素的方式:
	// 将键值对<"peach","桃子">插入map中,用pair直接来构造键值对
	m.insert(pair<string, string>("peach", "桃子"));
	// 将键值对<"peach","桃子">插入map中,用make_pair函数来构造键值对
	m.insert(make_pair("banan", "香蕉"));
	// 借用operator[]向map中插入元素
	/*
	operator[]的原理是:
	用<key, T()>构造一个键值对,然后调用insert()函数将该键值对插入到map中
	如果key已经存在,插入失败,insert函数返回该key所在位置的迭代器
	如果key不存在,插入成功,insert函数返回新插入元素所在位置的迭代器
	operator[]函数最后将insert返回值键值对中的value返回
	*/
	// 将<"apple", "">插入map中,插入成功,返回value的引用,将“苹果”赋值给该引用结果,
	m["apple"] = "苹果";

	// key不存在时抛异常
	//m.at("waterme") = "水蜜桃";
	cout << m.size() << endl;
	// 用迭代器去遍历map中的元素,可以得到一个按照key排序的序列
	for (auto& e : m)
		cout << e.first << "--->" << e.second << endl;
	cout << endl;
	// map中的键值对key一定是唯一的,如果key存在将插入失败
	auto ret = m.insert(make_pair("peach", "桃色"));
	if (ret.second)
		cout << "<peach, 桃色>不在map中, 已经插入" << endl;
	else
		cout << "键值为peach的元素已经存在:" << ret.first->first << "--->"
		<< ret.first->second << " 插入失败" << endl;
	// 删除key为"apple"的元素
	m.erase("apple");
	if (1 == m.count("apple"))
		cout << "apple还在" << endl;
	else
		cout << "apple被吃了" << endl;
}


3.3.3 multiset multimap

multiset:

multiset与set的区别是,multiset中的元素可以重复,set中value是唯一的!
 

multimap:

multimap和map的唯一不同就是:map中的key是唯一的,而multimap中key是可以
重复的。

multimap中没有重载operator[]操作


四、模拟实现set 和 map

4.1 底层红黑树

set与map底层都是红黑树,我们需要进一步给红黑树添加类模板参数和迭代器的实现!

4.1.1 红黑树类模板参数

红黑树模板参数:template <class T,class ValType,class GetVal>

分别代表的是Key值 存储数据类型 插入时搜索树比较的值

当我们使用map的时候,map的存储数据是键值对,我们插入数据需要比较的是Key值(也可以是val),不能比较键值对所以需要提供获取键值对其中一个值的接口!


4.1.2 红黑树迭代器实现

迭代器类成员就是结点的指针!

这里重点提一下如何实现迭代器的++的功能:通过迭代器++,我们可以中序遍历红黑树结点,获得升序数据!以上图举例算法实现::

首先我们迭代器从最左端结点开始,从1结点开始,也就是说begin的时候迭代器返回1结点指针。

++,先判断右边是否为空,如果右不为空,那么继续探索返回右孩子的Min结点,如果右节点左为空,则直接返回右孩子!

如果结点的右孩子为空,判断结点与其父节点的关系,如果其是父节点的右孩子,说明本棵树已经已经访问完全,需要向上寻找祖先,直到其是父节点的左孩子! 这里如何理解呢?我们中序遍历的顺序是 左 中 右,右为空说明左和中已经访问完全!

当我们的6结点左右访问完全,下一个结点是8,6为1的右,我们向上寻找祖先,通过迭代cur 与 parent ,当前cur为6,parent为1,让1变为cur,8变为parent,因为1是8的左,说明中还没访问,所以停止寻找,让指针指向8!

//红黑树迭代器
template <class ValType ,class ref,class ptr>
//                val       引用       指针
struct RBTreeiterator
{
	typedef TreeNode<ValType> Node;
	typedef RBTreeiterator<ValType,ref,ptr> self;
	typedef RBTreeiterator<ValType, ValType&, ValType*> iterator;
	Node* pnode;
	RBTreeiterator(Node* p)
		:pnode(p)
	{}

	//普通迭代器传给const迭代器
	RBTreeiterator(const iterator& it)
		:pnode(it.pnode)
	{}
	ref operator*()
	{
		return pnode->_val;
	}
	ptr operator->()
	{
		return &pnode->_val;
	}
	self& operator++()
	{
		//右不为空,寻找右的最左端,左端为空则返回自己
		if (pnode->_right)
		{
			Node* left = pnode->_right;
			while (left->_left)
			{
				left = left->_left;
			}
			pnode = left;
		}
		//右为空,寻找祖先
		else
		{
			Node* cur = pnode;
			Node* parent = cur->_parent;
			//孩子是父节点的右孩子,迭代cur 和 parent 
			while (parent && cur == parent->_right)
			{
				cur = parent;
				parent = cur->_parent;
			}
			//访问中
			pnode = parent;
		}
		return *this;
	}
	bool operator!=(const self& s)
	{
		return pnode != s.pnode;
	}
	bool operator ==(const self& s)
	{
		return pnode == s.pnode;
	}

红黑树迭代器接口: 

typedef RBTreeiterator<ValType, ValType&, ValType*> iterator;

typedef RBTreeiterator<ValType, const ValType, const ValType*> const_iterator;

iterator begin()
{
	Node* left = _root;
	while (left && left->_left)
	{
		left = left->_left;
	}
	return iterator(left);
}

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

//const迭代器
const_iterator begin()const
{
	Node* left = _root;
	while (left && left->_left)
	{
		left = left->_left;
	}
	return const_iterator(left);
}
const_iterator end()const
{
	return const_iterator(nullptr);
}

4.1.3 红黑树完整版

//红黑树结点颜色
enum colour
{
	RED,
	BLACK
};
template <class ValType>
//结点类
struct TreeNode
{
public:
	TreeNode(const ValType& val)
		:_val(val)
		,_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_colour(RED)
	{}
	ValType _val;
	TreeNode<ValType>* _left;
	TreeNode<ValType>* _right;
	TreeNode<ValType>* _parent;
	colour _colour;
};
//红黑树迭代器
template <class ValType ,class ref,class ptr>
struct RBTreeiterator
{
	typedef TreeNode<ValType> Node;
	typedef RBTreeiterator<ValType,ref,ptr> self;
	typedef RBTreeiterator<ValType, ValType&, ValType*> iterator;
	Node* pnode;
	RBTreeiterator(Node* p)
		:pnode(p)
	{}
	//普通迭代器传给const迭代器
	RBTreeiterator(const iterator& it)
		:pnode(it.pnode)
	{}
	ref operator*()
	{
		return pnode->_val;
	}
	ptr operator->()
	{
		return &pnode->_val;
	}
	self& operator++()
	{
		//右不为空,寻找右的最左端,左端为空则返回自己
		if (pnode->_right)
		{
			Node* left = pnode->_right;
			while (left->_left)
			{
				left = left->_left;
			}
			pnode = left;
		}
		//右为空,寻找祖先
		else
		{
			Node* cur = pnode;
			Node* parent = cur->_parent;
			//孩子是父节点的右孩子,迭代cur 和 parent 
			while (parent && cur == parent->_right)
			{
				cur = parent;
				parent = cur->_parent;
			}
			//访问中
			pnode = parent;
		}
		return *this;
	}
	bool operator!=(const self& s)
	{
		return pnode != s.pnode;
	}
	bool operator ==(const self& s)
	{
		return pnode == s.pnode;
	}
};
//红黑树
template <class T,class ValType,class GetVal>
class RBTree
{
public:
	typedef TreeNode<ValType> Node;
	typedef RBTreeiterator<ValType, ValType&, ValType*> iterator;
	typedef RBTreeiterator<ValType, const ValType, const ValType*> const_iterator;
	RBTree()
		:_root(nullptr)
	{}
	void RotateR(Node* parent)
	{
		Node* SubL = parent->_left;
		Node* SubLR = SubL->_right;
		parent->_left = SubLR;
		if (SubLR) SubLR->_parent = parent;
		Node* gparent = parent->_parent;
		if (parent == _root)
		{
			_root = SubL;
			SubL->_parent = nullptr;
		}
		else {
			if (parent == gparent->_left)
				gparent->_left = SubL;
			else gparent->_right = SubL;
			SubL->_parent = parent->_parent;
		}
		SubL->_right = parent;
		parent->_parent = SubL;
	}
	void RotateL(Node* parent)
	{
		Node* SubR = parent->_right;
		Node* SubRL = SubR->_left;
		parent->_right = SubRL;
		if (SubRL) SubRL->_parent = parent;
		Node* gparent = parent->_parent;
		if (parent == _root)
		{
			_root = SubR;
			SubR->_parent = nullptr;
		}
		else {
			if (parent == gparent->_left)
				gparent->_left = SubR;
			else gparent->_right = SubR;
			SubR->_parent = gparent;
		}
		SubR->_left = parent;
		parent->_parent = SubR;
	}
	void Inorder()
	{
		_Inorder(_root);
	}
	pair<iterator,bool> insert(const ValType& val)
	{
		GetVal getval;
		if (_root == nullptr)
		{
			_root = new Node(val);
			_root->_colour = BLACK;
			return make_pair(iterator(_root), true);
		}
		Node* cur = _root;
		Node* parent = nullptr;
		//查找插入点
		while (cur)
		{
			if (getval(cur->_val) < getval(val))
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (getval(cur->_val) > getval(val))
			{
				parent = cur;
				cur = cur->_left;
			}
			else return make_pair(iterator(cur), false);
		}
		//链接关系
		cur = new Node(val);
		Node* newnode = cur;
		cur->_parent = parent;
		if (cur->_val > parent->_val)
			parent->_right = cur;
		else parent->_left = cur;
		//插入后判断是否满足红黑树要求
		while (parent && parent->_colour == RED)
		{
			Node* gparent = parent->_parent;
			Node* uncle = nullptr;
			if (parent == gparent->_left)
				uncle = gparent->_right;
			else uncle = gparent->_left;
			//情况一
			if (uncle && uncle->_colour == RED)
			{
				parent->_colour = uncle->_colour = BLACK;
				gparent->_colour = RED;
				cur = gparent;
				parent = cur->_parent;
			}
			//情况二 + 情况三
			else {
				if (uncle == gparent->_right)
				{
					//情况二
					if (cur == parent->_left)
					{
						RotateR(gparent);
						gparent->_colour = RED;
						parent->_colour = BLACK;
					}
					//情况三 
					else {
						RotateL(parent);
						RotateR(gparent);
						gparent->_colour = RED;
						cur->_colour = BLACK;
					}
				}
				else {
					//情况二
					if (cur == parent->_right)
					{
						RotateL(gparent);
						gparent->_colour = RED;
						parent->_colour = BLACK;
					}
					//情况三
					else {
						RotateR(parent);
						RotateL(gparent);
						gparent->_colour = RED;
						cur->_colour = BLACK;
					}
				}
			}
		}
		//插入过程可能改变根的颜色,根必须为黑!!!
		_root->_colour = BLACK;
		return make_pair(iterator(newnode), true);
	}
	bool check(Node* root,int BlackNodeNum,int ref)
	{
		//走到空,判断此时黑节点累计数量是否满足标准
		if (root == nullptr)
		{
			if (BlackNodeNum != ref)
				return false;
			return true;
		}
		//连续出现两次红色结点,不满足规则
		if (root->_colour == RED && root->_parent->_colour == RED)
			return false;
		//每次遇到黑节点 ++黑节点个数
		if (root->_colour == BLACK)
		{
			BlackNodeNum++;
		}
		//递归左右子树
		return check(root->_left, BlackNodeNum, ref) && check(root->_right, BlackNodeNum, ref);
	}
	bool IsBalanceTree()
	{
		if (_root == nullptr)
			return true;
		if (_root->_colour == RED)
		{
			return false;
		}
		Node* left = _root;
		//计算一条路径的黑色结点个数,以它为标准审视每条路径
		int ref = 0;
		int BlackNodeNum = 0;
		while (left)
		{
			if (left->_colour == BLACK)
				++ref;
			left = left->_left;
		}
		return check(_root, BlackNodeNum, ref);
	}
	iterator begin()
	{
		Node* left = _root;
		while (left && left->_left)
		{
			left = left->_left;
		}
		return iterator(left);
	}
	iterator end()
	{
		return iterator(nullptr);
	}
	//const迭代器
	const_iterator begin()const
	{
		Node* left = _root;
		while (left && left->_left)
		{
			left = left->_left;
		}
		return const_iterator(left);
	}
	const_iterator end()const
	{
		return const_iterator(nullptr);
	}
private:
	void _Inorder(const Node* root)
	{
		if (root == nullptr)
			return;
		_Inorder(root->_left);
		//cout << (root->_val).second << ' ';
		cout <<"[" << (root->_val).first << "," << (root->_val).second <<"]" << endl;
		_Inorder(root->_right);
	}
	Node* _root;
};

4.2 set的模拟实现 

上面提到set的数据具有唯一性,所以set的迭代器也是const 迭代器,我们不能通过* 来修改set的数据! 

set的接口都是调用红黑树的接口,迭代器是红黑树的迭代器!

namespace wyz
{
	template <class K>
	class set
	{
	public:
		struct setGetVal
		{
			const K& operator()(const K& k)
			{
				return k;
			}
		};
		typedef RBTree<K, K, setGetVal> RBTree;
		typedef typename RBTree::const_iterator iterator;
		typedef typename RBTree::const_iterator const_iterator;

		pair<iterator, bool> insert(const K& k)
		{
			//注意ret类型中的迭代器是普通迭代器 
			pair<typename RBTree::iterator, bool> ret = _t.insert(k);
			//我们这里需要用到普通迭代器拷贝构造const迭代器!!!
			return make_pair(iterator(ret.first), ret.second);
		}
		void Inorder()
		{
			_t.Inorder();
		}
		bool IsBalanceTree()
		{
			return _t.IsBalanceTree();
		}
		iterator begin()const
		{
			return _t.begin();
		}
		iterator end()const
		{
			return _t.end();
		}
	private:
		RBTree _t; //底层是红黑树
	};
}

 4.3 模拟实现map

map特别之处在于它的 [] 重载 我们可以通过 对象.[key] 访问val! 

namespace wyz
{
	template <class K, class V>
	class map
	{
	public:
		//我们通过key值比较,决定插入的位置
		struct mapGetVal
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};
		typedef RBTree<K, pair<K, V>, mapGetVal> RBTree;
		typedef typename RBTree::iterator iterator;
		typedef typename RBTree::const_iterator const_iterator;
		pair<iterator, bool> insert(const pair<K, V>& kv)
		{
			return _t.insert(kv);
		}
		V& operator[](const K& key)
		{
			//这里绝妙就在构造匿名对象,如果没有对应的val,则插入V的匿名对象
			pair<iterator, bool> ret=insert(make_pair(key, V()));
			//返回val
			return ret.first->second;
		}
		//中序遍历
		void Inorder()
		{
			_t.Inorder();
		}
		//判断是否为红黑树
		bool IsBalanceTree()
		{
			return _t.IsBalanceTree();
		}
		iterator begin()
		{
			return _t.begin();
		}
		iterator end()
		{
			return _t.end();
		}
		const_iterator begin()const
		{
			return _t.begin();
		}
		const_iterator end()const
		{
			return _t.end();
		}
	private:
		RBTree _t;
	};
}

4.4 测试set和map

4.4.1 set 测试

void Test_set()
{
	int arr[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
	wyz::set<int> t;
	for (auto e : arr)
	{
		t.insert(e);
	}
	wyz::set<int>::iterator it = t.begin();
	while (it != t.end())
	{
		//(*it) += 10;
		cout << *it << endl;
		++it;
	}
}

不允许解引用修改val ! 



4.4.2 map测试 

void Test_map2()
{
	string arr[] = { "苹果", "西瓜", "香蕉", "草莓", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
	wyz::map<string, int> countMap;
	for (auto e : arr)
	{
		countMap[e]++;
	}
	wyz::map<string, int>::const_iterator it = countMap.begin();
	while (it != countMap.end())
	{
		cout <<it->first<<":"<< it->second << endl;
		++it;
	}
}

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值