c++STL——红黑树封装set与map:深入源码解析

用红黑树封装set和map

上文我们讲解了如何实现一个红黑树,本文将模拟实现STL库中的set和map这两个容器。因为它们的底层也是由红黑树进行实现的。

源码剖析

主要模拟实现的是SGI-STL30版本源代码,其内部map和set的源代码在map/set/stl_map.h/stl_set.h/stl_tree.h等几个头文件中。

在这里插入图片描述
在这里插入图片描述
打开map和set两个文件发现,内部东西并不多,内容其实都被包在了stl_tree.h和其他文件中了。我们先打开stl_tree.h来看,里面有红黑树的源码。

// set
#ifndef __SGI_STL_INTERNAL_TREE_H
#include <stl_tree.h>
#endif
#include <stl_set.h>
#include <stl_multiset.h>

// map
#ifndef __SGI_STL_INTERNAL_TREE_H
#include <stl_tree.h>
#endif
#include <stl_map.h>
#include <stl_multimap.h>

// stl_set.h
template <class Key, class Compare = less<Key>, class Alloc = alloc>
class set {
public:
	// typedefs:
	typedef Key key_type;
	typedef Key value_type;
private:
	typedef rb_tree<key_type, value_type,
		identity<value_type>, key_compare, Alloc> rep_type;
	rep_type t; // red-black tree representing set
};

// stl_map.h
template <class Key, class T, class Compare = less<Key>, class Alloc = alloc>
class map {
public:
	// typedefs:
	typedef Key key_type;
	typedef T mapped_type;
	typedef pair<const Key, T> value_type;
private:
	typedef rb_tree<key_type, value_type,
		select1st<value_type>, key_compare, Alloc> rep_type;
	rep_type t; // red-black tree representing map
};


// stl_tree.h
struct __rb_tree_node_base
{
	typedef __rb_tree_color_type color_type;
	typedef __rb_tree_node_base* base_ptr;
	color_type color;
	base_ptr parent;
	base_ptr left;
	base_ptr right;
};
// stl_tree.h
template <class Key, class Value, class KeyOfValue, class Compare, class Alloc
	= alloc>
class rb_tree {
protected:
	typedef void* void_pointer;
	typedef __rb_tree_node_base* base_ptr;
	typedef __rb_tree_node<Value> rb_tree_node;
	typedef rb_tree_node* link_type;
	typedef Key key_type;
	typedef Value value_type;
public:
	// insert⽤的是第⼆个模板参数左形参
	pair<iterator, bool> insert_unique(const value_type& x);
	// erase和find⽤第⼀个模板参数做形参
	size_type erase(const key_type& x);
	iterator find(const key_type& x);
protected:
	size_type node_count; // keeps track of size of tree
	link_type header;
};
template <class Value>
struct __rb_tree_node : public __rb_tree_node_base
{
	typedef __rb_tree_node<Value>* link_type;
	Value value_field;
};

当前的水平直接看源码的细节是非常困难的。我们能做的只是稍微看个大概,梳理一下逻辑。比如类和类之间的关系,类里面的功能,或者函数功能作用等。在这里我们截取了几个比较重要的部分来看。

直接看可能看不太出有什么关系,我们通过下面这一张图来梳理一下:
在这里插入图片描述
这个stl_tree.h中的rb_tree其实就是红黑树,内部好几个模板参数。Key就是搜索的那个关键字,Value是存储的类型。这个是用来区分存储的是否为pair类。KeyOfValue这个我们可以等下说,当前并不是很重要。内部还有个rb_tree_node,这个是红黑树的节点,我们发现,这个节点也是被声明成了类模板,存储的数据是Value类型的。也就是说,大佬们在写这个代码的时候,不想写那么多个版本的,直接把节点的存储值变成了Value类型。让编译器自己去判断。

对于set来说,它的模板参数Key就是set存储的关键字依据,后面两个模板参数分别是内存池的和搜索树的比较逻辑。一般我们使用默认的就可以了。set中的保护成员是一个rb_tree,但是传给rb_tree的前两个模板参数分别是key_type和value_type,但它们都是由Key这个参数typedef来的,所以是一样的。

我们再来看看map,第一个模板参数仍是Key,第二个模板参数是T,T又被重命名为mapped_type。保护成员也是一个rb_tree,只不过第一个参数传的Key,第二个是value_type,也就是一个被重命名的pair类。

至此我们大致分析有了个底,我们发现底层的实现还是比较复杂的,模板里面套了模板。

我们现在肯定会有疑问,为什么要写的这么复杂?

实现框架分析

我们来一起分析一下如此复杂是为何。

如果按照以往我们的实现方式,很容易把一个树写死。特别是针对与map和set,一个传的是key,另一个传入的是pair<Key, Value>。但是这都是存储的数据类型罢了。但是问题就在于,pair类的比较大小使用库里面重载的那个是不符合要求的。传入的值是pair的时候,需要通过pair类的first成员来比较。那岂不是还要专门针对于pair类的来写一个红黑树。这样太浪费了。大佬们是不会容忍这种事情发生的。代码也十分的冗余。

所以底层采用的方式就是,先把红黑树rb_tree这个类模板给写好,然后map和set底层使用的方式就是和之前实现栈和队列那样的适配器方式,所有的操作均由底层的红黑树去完成。上层的容器只是对底层的红黑树进行一些操作。这样子实现就会非常简单。

但是说实话:SGI-STL30版本这个里面实现的代码命名风格太乱了,很多时候会与我们以往认知的概念有偏差。所以我们自行实现的时候需要规范一下我们的命名风格。
我们规定map和set中的关键字的模板参数为K,存储的数据类型为T,map中与K形成映射关系的模板参数是V,这样子就规范的多了。

我们也不会去学习库里面那样的一堆的typedef操作。我们将自己实现的东西放在一个叫myspace的命名空间中,防止引起命名冲突。

改进框架

这个部分我们将上一节实现的红黑树的框架进行修改

第一步,对于我们自己实现的RBtreeNode,我们需要改进一下数据类型,将其声名为一个适用于各种数据类型(如int,double,char,自定义类型等)的版本。所以将节点中存储的数据类型统统用参数T来代替:

enum color{
	BLACK,
	RED
};

template<class T>
//这个T就是数据的类型,如果是map会传pair类进来
//当然set也可以传pair,只不过和map中的就有区别了 一般也不会这么用
struct RBTreeNode {
    T _data;
	color _col;

	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right; 
	RBTreeNode<T>* _parent; 

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

第二步我们就需要修改一下RBTree的框架了。但是这个时候会面临着一些问题:
1.RBTree里面肯定会有树节点的操作,所以是必然需要传入参数T的。代表着节点中存储数据的类型。但是正式因为这样子,把原来的Key和Value泛化成T这个参数。但是内部的查找等接口也是要通过Key(K)这个参数来找,所以也是得传入K这个模板参数。

2.但是现在还有一个问题,就是现在数据类型泛化成T后,比较逻辑怎么办?特别是map这个容器。map在容器内将pair类传给RBTree的第二个参数T,pair是支持比较逻辑,但是并不是我们想要的。我们想要的是比较Key。

set好办,set第一个和第二个参数传入的都是K,直接能比较。map是需要特殊处理的。但是又因为set和map都是以适配器模式来实现的,底层都是同一个类模板。所以我们需要想一个办法,能够让数据通过这种特定的方式来比较。

这里我们就很自然而然地想到了使用仿函数了。我们可以根据这个仿函数,返回传入数据的Key值。这个仿函数我们就叫它KeyOfT。如果是在set中,我们可以将数据传入,直接返回Key。如果是map,我们是很清楚的知道map中的数据一定是pair,那就返回pair的first成员变量就可以了。然后通过这个仿函数,就可以很轻松的把key取出来。

但是这个仿函数的实现应该在哪呢?是表层容器中还是底层红黑树呢?当然是表层的容器中了,因为底层没办法区分出节点中数据类型是什么。实现在表层底层怎么用呢?当然是作为模板参数传入了,就像我们之前实现优先级队列那样,把默认的比较逻辑传入。

//map.h
template<class K, class V>
class map {

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

private:
	//不支持修改key,所以将map中的pair中的第一个参数设置为const
	RBTree<K, pair<const K, V>, MapKeyOfT> _t;
};
namespace myspace {
	
	template<class K>
	class set {

		struct SetKeyOfT {
			const K& operator()(const K& key) {
				return key;
			}
		};
	public:

		
	private:
		//不允许修改key
		RBTree<K, const K, SetKeyOfT> _t;
	}; 

}
//RBTree.h
template<class K, class T, class KeyOfT>
class RBTree {
public:
	using Node = RBTreeNode<T>;    

	//这里相较于之前实现的要稍微改动一下
	pair<Iterator, bool> Insert(const T& data) {
		if (_root == nullptr) {
			_root = new Node(data);  
			_root->_col = BLACK; 
			++_Node_num;
			return { Iterator(_root, _root), true }; 
		}

		//先按照二叉树的逻辑插入
		KeyOfT kot;
		Node* parent = _root, * cur = _root;
		while (cur) {
			if (kot(data) > kot(cur->_data))
				cur = cur->_right;
			else if (kot(data) < kot(cur->_data))
				cur = cur->_left;
			else return { Iterator(cur, _root),false };

			if (cur) parent = cur;
		}
		//此时cur指向被插入的位置 parent指向被插入位置的父亲节点
		cur = new Node(data);
		Node* ret = cur;//记录一下返回值
		if (kot(data) > kot(parent->_data)) parent->_right = cur;    
		else parent->_left = cur;
		cur->_parent = parent;
		++_Node_num;   
  


		//根据红黑树的规则来调整这个树
		//此时cur指向被插入节点 parent指向其父亲节点
		while (parent && parent->_col == RED) {
			Node* grandfather = parent->_parent;

			//  g
			// p u
			// c在p的左边或者右边 
			if (parent == grandfather->_left) {
				Node* uncle = grandfather->_right;

				if (uncle && uncle->_col == RED) {
					//这种情况只需要变色 不断向上调整就可以了(uncle存在且为红)
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					//继续向上调整
					cur = grandfather;
					parent = cur->_parent;
				}

				else {
					//叔叔不存在或者存在且为黑 都需要进行旋转 + 变色操作
					//但是需要判断一下是单旋还是双旋
					if (cur == parent->_left) {
						//  g
						// p  u
						//c
						//单旋 + 变色
						RotateR(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;

					}
					else {
						//  g
						// p  u
						//  c
						//双旋 + 变色
						RotateL(parent);
						RotateR(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}

					break;
				}
			}

			// g
			//u  p
			//c在p的左边或者右边
			else {
				Node* uncle = grandfather->_left;

				if (uncle && uncle->_col == RED) {
					//这种情况只需要变色 不断向上调整就可以了(uncle存在且为红)
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					//继续向上调整
					cur = grandfather;
					parent = cur->_parent;
				}

				else {
					//此时叔叔不存在或者存在且为黑色

					if (cur == parent->_right) {
						// g
						//u  p
						//     c
						//此时需要进行单旋 + 变色 
						RotateL(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}

					else {
						// g
						//u  p
						//  c
						//双旋 + 变色
						RotateR(parent);
						RotateL(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}

					break;
				}

			}
		}

		_root->_col = BLACK;
		return { Iterator(ret, _root), true };

	}

private:
	
	//旋转代码 有用
	void RotateR(Node* parent) {
		//右单旋其实就是把cur的右孩子给parent作为左孩子
		//再让cur作为根节点 parent作为cur的右孩子
		//还得修改一下被改动节点的_parent指针指向
		//然后需要将新的子树根节点与原来指向此子树的根节点的节点进行链接
		Node* Parent = parent;
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		Node* P_Parent = Parent->_parent;

		Parent->_left = subLR;
		//subLR可能为空 所以需要判断
		if (subLR != nullptr) subLR->_parent = Parent;

		subL->_right = Parent;
		Parent->_parent = subL;

		//链接原来的AVLTree
		//但是这时候有一个问题 即原来的子树根节点的父亲P_Parent可能是空(对应此时Parent是整个树的根节点)
		if (Parent == _root) {
			_root = subL;
			subL->_parent = nullptr;
		}
		else {
			//此时的新根节点subL不知道是应该插入在P_Parent的左边还是右边 需要判断一下
			if (P_Parent->_left == Parent) P_Parent->_left = subL;
			else P_Parent->_right = subL;

			subL->_parent = P_Parent;
		}

	}

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

		Node* P_Parent = Parent->_parent;

		Parent->_right = subRL;
		if (subRL != nullptr) subRL->_parent = Parent;

		subR->_left = Parent;
		Parent->_parent = subR;

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

			subR->_parent = P_Parent;
		}

	}

	Node* _root = nullptr;//根节点指针
	size_t _Node_num = 0;//节点个数
};

框架其实就是对比较逻辑进行修改了一下。当然上层封装容器的时候我们会发现,map和set传给T的那个参数好像有些不一样:
对于set,传的是const K,对于map,传的是pair<const K, V>。这是为什么?因为map和set不支持对内部数据的key进行修改,这样子会破坏搜索树的性质。

库里面不是这么做的,现在是讲不明白的,这一点我们反正迭代器实现部分来讲解。

其余的变化不大。只不过这里我们实现成了像文档中的那样,插入后返回的是一个pair类:
这里的逻辑是这样的:因为map和set不支持数据重复,所以会有插入失败的情况。插入成功就返回由新插入位置的迭代器和true构成的pair,反之可能插入的数据重复了。那就由返回重复数据的位置迭代器和false构成的pair。当然我们这里并没有实现迭代器。这个在本篇文章的后面部分会讲解。当前可以先理解为插入返回true,反之返回false就可以了。

迭代器实现

上面对于RBTree提到了迭代器这个概念,本章节就来重点讲解一下迭代器的实现。

我们先来厘清迭代器的使用。还是面临着一个问题,迭代器写在哪?是写在RBTree中还是分别写在map和set中?这时候我们就需要好好分析一下。如果是分别写在上层的容器中,我们就需要写两遍类似的东西了。map和set迭代器遍历的本质都是它们的适配器RBTree _t,所以写在红黑树底层不是更好吗?上层再针对于不同情况封装一下其实是最好的。使用适配器模式最大的方便之处就在于可以直接调用底层的接口来实现功能。这早在实现栈和队列部分就已经领略到了,所以我们尽量往这方面靠。

所以现在的任务就是,给RBTree实现迭代器。为了有所区分,RBTree这一层的代码我都采用驼峰式命名法,以区分上层接口名。

正向迭代器

在这一层我们实现两个迭代器,一个叫Iterator,另一个叫ConstIterator。分别作为RBTree的正向的迭代器的不同版本。

正向迭代器基础框架

对于这种节点的迭代器,我们早在list的模拟实现中就完成过。本质就是把一个只想节点的指针放在类里面进行封装,然后针对于需求实现功能。所以我们声名一个类:

struct RBTree_Iterator{
	using Node = RBTreeNode<T>;
	
	Node* _node;
};

但是我们之前在list模拟实现部分说到,其实普通迭代器和const的迭代器也就是在解引用或者使用操作符->访问元素的时候有所区别。因为加const的迭代器的本质是不能修改迭代器指向的内容。但是我们又不希望写两份类似的代码出来,怎么办呢?

处理办法就是将不同的那部分作为模板参数,通过外界传参来控制:

template<class T, class Ref, class Ptr>
struct RBTree_Iterator {
	using Node = RBTreeNode<T>;
	using Self = RBTree_Iterator<T, Ref, Ptr>;

	Node* _node;
	
	//构造
	RBTree_Iterator(Node* node, Node* root)
		:_node(node),
	{}
};

这里把迭代器这个名字重命名一下,因为太长了不方便写。

功能实现

我们现在就来实现一些迭代器基础的功能。首先红黑树的二叉树是双向迭代器,支持++和–操作,但不支持+/-操作。这是我们要知道的。

1.首先需要实现一下迭代器的比较功能

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

bool operator!=(const Self& self) const{
	return !(*this == self);
}

这个部分非常简单,比较两个迭代器是否相同,只需要比较迭代器内的指针就可以了。比较不同就是直接对相等的逻辑取反。我个人更喜欢实现一个功能后其他复用,因为这样子如果比较逻辑有变只需要修改主逻辑就可以了。

2.再来看解引用功能

Ref operator*() const{
	assert(_node != nullptr);
	return _node->_data;
}

Ptr operator->() const{
	assert(_node != nullptr);
	return &(_node->_data);
}

这里其实和list中的迭代器解引用没什么区别。对于->的重载这里再提及一下,这里返回的是节点中数据部分的地址。也就是指向数据的。正常来说箭头访问是对于自定义类类型(class/struct)这种数据类型使用的。外层调用的时候其实就是:迭代器 -> 访问数据,其实这样正常来说语法是错误的。应该再使用一次箭头。但是编译器做了特殊处理,直接省略掉了,所以可以直接通过迭代器使用->来访问节点中的数据。

3.最重要的就是++/–操作
对于迭代器来讲,最重要的操作就是++和–的重载。在链表中很简单,直接让迭代器中的那个指针移动到下一个节点的位置了。对于序列式容器当然可以这么玩。但是RBTree是个关联式容器,不可能像list那样做的。

我们在map和set的使用中讲过,它们的迭代器遍历出来是中序。因为二叉搜索树的中序遍历是一个升序的序列(默认比较逻辑下)。所以我们实现中序遍历。

但是真的是像以前那样用递归来走中序遍历,然后依次移动指针吗?那当然不是。递归的中序遍历是针对于整个树而言的。现在是要让_node这个指针按照中序遍历,依次的在RBTree上移动。用递归其实很难办。所以需要想出一个非递归的办法。

然后再来搞清楚,迭代器的开始和结束位置分别在哪:
对于正向迭代器,开始位置就是整个树的最左边节点。结束位置呢?是最右节点吗?当然不是。迭代器的要求都是左闭右开,最右节点作为迭代器结束位置会导致这个位置用不到。我们先说结论,迭代器的结束位置是空。也就是让迭代器里面的指针置空。我们下面会证明。

然后现在就可以开始探讨如何进行++操作了。假设当前迭代器指向某个节点,如果要走正向的中序遍历,它应该怎么办?中序遍历就是左根右的顺序。但我们知道的是此时迭代器此时一定处于所有没有被遍历到的节点的最左边(某个局部子树的根节点)。它的左边一定是被遍历过的了。因为对于所有的子树都要满足左根右这个顺序遍历。

也就是说,此时处在一个局部的根结点上,左子树一定是遍历完的,那再++操作就需要往此时局部的右子树去走。这个时候有两种可能:

  1. 右子树不为空,那就需要往右边的最左走
    如果右子树不为空,这很简单,走到右子树的最左节点就可以了。为什么?因为右边不为空,右边也没有被遍历过,现在的目标就是对右子树进行中序遍历。右子树的中序遍历不也是对一个树进行中序遍历吗?那也是从这个树的最左结点开始。这个很好理解.

  2. 右子树为空,那就需要往上走
    在这里插入图片描述
    比如在25这个节点,再++一次,发现右边为空,那就要往上走,直接走到30。
    但是不是一定走到自己的父亲节点的,比如15这个节点,很明显10是在它之前被遍历过的。下一个节点应该是18。直接看很容易看出来,但是怎么找到那个位置呢?

我们理清一下思路:迭代器遍历是中序——左根右。此时我们发现15和25的最大区别是,一个是父亲的右孩子,一个是父亲的左孩子。如果是父亲的左孩子就简单,因为走到某个节点x后,左边一定是被遍历过的,右边如果为空,那就说明以这个节点x为根的子树已经被遍历完了。x又是左子树。按照左根右的顺序,自然走到x的父亲位置就可以了。

但是如果是右孩子y呢?也是一样的,y的右为空,以y为根节点的树被遍历完了。按照左根右逻辑来说,y的父亲节点也是被遍历完的,y的父亲节点yParent的左边就更不用说了。故以yParent为根的子树也被遍历完了。那还得继续往上走。但是也是一样的,yParent有可能作为右子树,那也是一样的还得网上找。知道找到个节点为根的左子树的时候,这个时候的根就是下一个节点。对于这个情况简称为:
找到孩子是父亲左孩子的时候的那个祖先节点就是中序要问题的下一个结点。

我们就拿上面那个例子验证一下:
1.从整个树的最左节点10开始遍历,10的右边不为空,走到10的右子树最左边的15。
2.15右边为空,且为10的右孩子,一直向上走找到10为15的左孩子,下一个节点是18。
3.18的右边不为空,走到右子树最左的25。
4.25的右边为空,为30的左孩子,走到30。
5.30的右边不为空,走到右边的最左的35。
6.35的右边为空,且为40的左孩子,走到40。
7.40的右边不为空,走到40右边最左的50。
8.50右边为空,为40右孩子,需要不断向上找。

找的过程中发现,找到根了,但是根并不是谁的左右节点怎么办呢?
经过一通分析,我们知道这种找不到的情况只可能出现在遍历完最后一个节点回退的情况下。其余的时候找的到,就让parent的位置给_node。现在为空了怎么办呢?直接让空作为迭代器的结束不就可以了,我们只需要控制一下parent为空的时候不再向上寻找就好了。parent为空也不怕,也可以把这个位置给_node。

所以得到我们的代码:

//前置
Self& operator++() {
	Node* cur = _node;
	if (cur->_right) {
		cur = cur->_right;
		while (cur->_left) {
			cur = cur->_left;
		}
		_node = cur;
	}
	else {
		//右边为空 说明这个子树访问完成
		Node* parent = cur->_parent;
		while (parent && cur == parent->_right) {
			cur = parent;
			parent = parent->_parent;
		}
		//此时有两种情况
		//cur在parent的左边 或者 cur在根节点parent为空
		_node = parent; 
	}
	return *this;
}

//后置 直接复用前置逻辑就好了
Self operator++(int) {
	Self it = *this;
	++(*this);
	return it;
}

对–的逻辑其实就是和++的逻辑相反就可以了,就是按照右根左的顺序来遍历:

Self& operator--() {
	Node* cur = _node;
	if (_node == nullptr) {
		Node* proot = _root;
		while (_root && _root->_right) {
			_root = _root->_right;
		}
		_node = _root; 
	}
	//有一个问题就是 如果只是将++的逻辑反过来会出问题 
	//从end位置开始走的会出问题
	//因为我们的写法是end位置是空指针
	else if (cur->_left) {
		cur = cur->_left;
		while (cur->_right) {
			cur = cur->_right;    
		}
		_node = cur;
	}
	else {
		//此时左边为空  又因为是倒着走
		//说明此时此时这个子树倒着遍历走完了
		Node* parent = cur->_parent;
		while (parent && cur == parent->_left) {
			cur = parent;
			parent = parent->_parent;
		}
		_node = parent;
	}

	return *this;
}

Self operator--(int) {
	Self it = *this;
	--(*this);
	return it;
}

也就是判断左边是否为空。不为空就去找左边的最右边。反之据往上走,根++的逻辑相反。
但是我们发现,这里多了一个判断,这是为什么?

这是为了避免有人会用正向迭代器反着走,即从迭代器结束位置开始向前走。但是迭代器结束位置时,内部指针为空。是不能进行任何移动或者访问的。这就需要我们特殊处理一下,如果是从结束位置往前走,就去找整个树的最右节点就可以了。

但是这样做是需要传入整个树的树根才能做到的。所以为了支持这种情况,需要给迭代器多一个成员变量Node* _root。专门用来记录整个树的根节点位置。

正向迭代器的代码展示(合并版本)
//迭代器部分
template<class T, class Ref, class Ptr>
struct RBTree_Iterator {
	using Node = RBTreeNode<T>;
	using Self = RBTree_Iterator<T, Ref, Ptr>;

	Node* _node;
	Node* _root;//为了支持在end()处--
	
	RBTree_Iterator(Node* node, Node* root)
		:_node(node),
		_root(root)
	{}


	Self& operator++() {
		Node* cur = _node;
		if (cur->_right) {
			cur = cur->_right;
			while (cur->_left) {
				cur = cur->_left;
			}
			_node = cur;
		}
		else {
			//右边为空 说明这个子树访问完成
			Node* parent = cur->_parent;
			while (parent && cur == parent->_right) {
				cur = parent;
				parent = parent->_parent;
			}
			//此时有两种情况
			//cur在parent的左边 或者 cur在根节点parent为空
			_node = parent; 
		}
		return *this;
	}

	Self operator++(int) {
		Self it = *this;
		++(*this);
		return it;
	}

	Self& operator--() {
		Node* cur = _node;
		if (_node == nullptr) {
			Node* proot = _root;
			while (_root && _root->_right) {
				_root = _root->_right;
			}
			_node = _root; 
		}
		//有一个问题就是 如果只是将++的逻辑反过来会出问题 
		//从end位置开始走的会出问题
		//因为我们的写法是end位置是空指针
		else if (cur->_left) {
			cur = cur->_left;
			while (cur->_right) {
				cur = cur->_right;    
			}
			_node = cur;
		}
		else {
			//此时左边为空  又因为是倒着走
			//说明此时此时这个子树倒着遍历走完了
			Node* parent = cur->_parent;
			while (parent && cur == parent->_left) {
				cur = parent;
				parent = parent->_parent;
			}
			_node = parent;
		}

		return *this;
	}
	
	Self operator--(int) {
		Self it = *this;
		--(*this);
		return it;
	}

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

	bool operator!=(const Self& self) const{
		return !(*this == self);
	}

	Ref operator*() const{
		assert(_node != nullptr);
		return _node->_data;
	}

	Ptr operator->() const{
		assert(_node != nullptr);
		return &(_node->_data);
	}

};

反向迭代器

反向迭代器其实不是重点,但是在这里还是说一下。

反向迭代器的分析和代码实现

反向迭代器我们当然可以直接像和正向迭代器那样的实现一样,写一个类似。只不过++/–的操作反过来而已。但是这样写非常冗余,也完全没有必要。

反向迭代器和正向不同的地方也就是++/–的操作,其余的解引用什么都是一样的。那我们完全可以使用适配器模式啊。只不过上层调用++,底层调用–。反之调用++。这不就很轻松的解决了问题吗?所以我们根本不需要再来实现一个类似类,直接使用适配器模式就可以了:

//封装正向迭代器为反向迭代器
template<class T, class Ref, class Ptr>
struct RBTRee_ReverseIterator {
	using Node = RBTreeNode<T>;
	using Self = RBTRee_ReverseIterator<T, Ref, Ptr>;

	RBTree_Iterator<T, Ref, Ptr> _it;

	//这里的构造一样要把根节点传入进来 因为这里是调用正向迭代器的构造
	RBTRee_ReverseIterator(Node* node,	Node* root)
		:_it(RBTree_Iterator<T, Ref, Ptr>(node, root))
	{}

	Self& operator++() {
		--_it;
		return *this;
	}

	Self operator++(int) {
		Self it = *this;
		++(*this);
		return it;
	}

	Self& operator--() {
		//为了避免使用反向迭代器倒着走(其实就是正向)
		if (_it._node == nullptr) {
			Node* cur = _it._root;
			while (cur && cur->_left) {
				cur = cur->_left;
			}
			_it._node = cur;
		}
		else ++_it;

		return *this;
	}

	Self operator--(int) {
		Self it = *this;
		--(*this);
		return it;
	}

	bool operator==(const Self& self) const {
		return _it == self._it;
	}

	bool operator!=(const Self& self) const {
		return !(*this == self);
	}

	Ref operator*() {
		return *_it; 
	}
	
	Ptr operator->() {
		return _it.operator->();
	}
};

除了箭头这个调用的方式形式不太一样之外,其余的都是直接复用正向迭代器的逻辑。在这里就不多讲了。

将迭代器封装至上层容器

使用迭代器的时候,是需要给其实位置的,也就是那几个迭代器接口。

秉持着一贯的原则,适配器模式下上层尽可能都去调用底层的接口。所以获取迭代器的接口应该写在RBTree中。RBTree会通过这几个迭代器的接口返回迭代器。

上层是如何使用的呢?我们知道,迭代器的具体类型其实我们是不知道的。具体是普通版本还是const版本,每个迭代器内节点指针指向的节点中存储的数据是什么类型其实也都不知道。这需要在外界去传模板参数去决定迭代器的类型。底层的RBTree会根据上层传的模板参数来推演构造出迭代器:

using Iterator = RBTree_Iterator<T, T&, T*>;
using ConstIterator = RBTree_Iterator<T, const T&, const T*>;
using ReverseIterator = RBTRee_ReverseIterator<T, T&, T*>;
using ConstReverseIterator = RBTRee_ReverseIterator<T, const T&, const T*>;

我们需要在RBTree中重命名这四中迭代器的名字,分别代表着RBTree的各种版本迭代器。

然后给出在RBTree中的迭代器的接口:

Node* LeftMost() const{
	Node* cur = _root;
	while (cur && cur->_left) {
		cur = cur->_left;
	}
	return cur;
}

Node* RightMost() const{
	Node* cur = _root;
	while (cur && cur->_right) {
		cur = cur->_right;
	}
	return cur;
}

Iterator Begin() {
	Node* cur = LeftMost();
	return Iterator(cur,_root);
}

Iterator End() {
	return Iterator(nullptr, _root);
}

ConstIterator Begin() const {
	Node* cur = LeftMost();
	return ConstIterator(cur, _root); 
}

ConstIterator End() const{
	return ConstIterator(nullptr, _root);
}

ReverseIterator RBegin() {
	Node* cur = RightMost();
	return ReverseIterator(cur, _root);
}

ReverseIterator REnd() {
	return ReverseIterator(nullptr, _root);
}

ConstReverseIterator RBegin() const {
	Node* cur = RightMost();
	return ConstReverseIterator(cur, _root);  
}

ConstReverseIterator REnd() const{
	return ReverseIterator(nullptr, _root);
}

因为迭代器可能频繁的找最左节点和左右节点,所以直接把最左节点和最右节点抽出。

注意:这里的取最左节点和最右节点函数一定要修饰成const成员函数。正常使用时不会出现问题的。坏就坏在我们下面写获取迭代器函数的时候。为了区分不同版本的Begin和End,对于const迭代器实现的,会在后面加入一个const。const修饰成员函数的本质是让函数中的隐式 class(类名) * const this的this指针变成const class(类名) * const this的形式,这样就能区分不同版本的迭代器获取函数了。

也正是如此,在const迭代器获取中,如果调用了非const的LeftMost和RightMost函数,等同于把const class(类名) * const this传给了class(类名) * const this。之前说过,这有权限放大的问题(前面的形式能修改指向的数据,后面的不行)。所以需要实现成const成员函数才可以避免这个问题(理由:权限可以平移和缩小)。

这个时候来看看上层如何调用:
底层的RBTree已经实现好它自己的迭代器了。上层的容器直接调用内部的接口不就行了吗?我们把上层的迭代器的命名风格保持和STL库中的一一致就可以了。

然后上层的迭代器本质就是通过传入不同的模板参数构造出RBTree,这些模板参数又会进一步传给RBTree中声名的迭代器的模板参数。这样子就能构造出一个迭代器了。其实这里被封装的比较多次。很容易搞混。

我们只需要知道,上层使用普通迭代器,其实就是使用了下层的普通迭代器。上层用什么版本,下层跟着用。对上层迭代器的操作会转化为对下层的迭代器的使用。

一切的根本原因就是因为上层将下层的迭代器重命名了。本质上还是一个东西。

//map.h中map
typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>
::Iterator iterator; 

typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>
::ConstIterator const_iterator;

typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>
::ReverseIterator reverse_iterator;  

typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>
::ConstReverseIterator const_reverse_iterator;

iterator begin() {
	return _t.Begin();
}
const_iterator begin() const {
	return _t.Begin();
}
iterator end() {
	return _t.End(); 
}
const_iterator end() const {
	return _t.End();
}

reverse_iterator rbegin() {
	return _t.RBegin();
}
const_reverse_iterator rbegin() const {
	return _t.RBegin();
}
reverse_iterator rend() {
	return _t.REnd();
}
const_reverse_iterator rend() const {
	return _t.REnd();
}
//set.h中set
typedef typename RBTree<K, const K, SetKeyOfT>
::Iterator iterator;

typedef typename RBTree<K, const K, SetKeyOfT>
::ConstIterator const_iterator;

typedef typename RBTree<K, const K, SetKeyOfT>
::ReverseIterator reverse_iterator;

typedef typename RBTree<K, const K, SetKeyOfT>
::ConstReverseIterator const_reverse_iterator;

iterator begin() {
	return _t.Begin();
}
const_iterator begin() const {
	return _t.Begin();
}
iterator end() {
	return _t.End();
}
const_iterator end() const {
	return _t.End();
}

reverse_iterator rbegin() {
	return _t.RBegin();
}
const_reverse_iterator rbegin() const {
	return _t.RBegin();
}
reverse_iterator rend() {
	return _t.REnd();
}
const_reverse_iterator rend() const {
	return _t.REnd();
}

我们在map和set中重命名这四个类型。调用接口的返回值和函数返回值是一样的。只不过是给typedef了,这点需要我们注意一下。

迭代器的默认成员函数

正常来说,默认成员函数在不符合要求的情况下是需要自己写的。但是迭代器不用,为什么?

因为迭代器不需要深拷贝。指向某个位置的迭代器赋值给另外一个的时候,就是要构造出的新的迭代器指向同一个位置。这个和以前的其他类不太一样。迭代器怎么样都是在同一个容器上遍历的。不能说是构造一个迭代器,就得深拷贝一次容器。那样代价太大了。

所以迭代器这个类的默认成员函数统统不用写。

与STL库中(SGI-STL30)区别

这里我们简单提及一下就可以了。

这个版本的STL库中不是像我们这样写的。SGI-STL30中对于map和set的普通迭代器其实是通过const_iterator这个typedef过来的。这是库里面的做法。但是这个做法很容器出问题。表层是将普通迭代器iterator搞成和const_iterator一样了。本质上我们就可以认为,上层的普通迭代器是下层的const迭代器了。

这样做的问题就在于,上层调用接口的时候,调用的是RBTree中的迭代器接口。返回的迭代器如果是底层的普通迭代器,那就麻烦了,如这个场景:

在这里插入图片描述
在这里插入图片描述

这个时候,上层的begin()返回的是经过typedef得到的iterator,其实就是下层的ConstIterator。但是t.begin()可能会返回下层的iterator,这二者之间不能直接转换的。我们必须支持一个转换才行。这就非常复杂了。所以尽量不要这么写。

当然还有区别的就是,SGI-STL30对于红黑树的迭代器的结束位置并不是nullptr,而是使用了一个哨兵位节点:
在这里插入图片描述
这就是为什么库中的版本只有header这个指针。这样子找最左节点和最右节点方便了,但是维护起来也是难度不小。所以这个方式了解一下即可。我们不用这个方式也能实现。

map支持[ ]重载

这个时候我们就得知道c++标准库是怎么实现[ ]的重载的。

c++执行的方式是,强制插入,返回一个pair<iterator, bool>的类对象。
插入成功,迭代器指向插入位置,bool类型为true
插入失败,迭代器返回已有数据位置,bool类型为false

所以这就是为什么在RBTree中提前修改了返回值。就是为了依照库中方括号的实现来走的逻辑。然后实现[ ]的重载就很简单了:

V& operator[](const K& key) {
	pair<iterator, bool> pr = _t.Insert( make_pair(key, V()));
	iterator pos = pr.first;
	return pos->second;
}

map中的[ ]重载返回值是map中存储的数据的那个pair的第二个值得引用,也就是V。

map、set、RBTree的完整代码展示

当然,里面可能还会有其它的一些接口,比如构造函数、析构、拷贝构造、赋值重载、查找、节点个数等接口。这些都很简单,在这里就不多说了

//map
template<class K, class V>
class map {

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

	typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>
	::Iterator iterator; 
	typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>
	::ConstIterator const_iterator;
	typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>
	::ReverseIterator reverse_iterator;  
	typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>
	::ConstReverseIterator const_reverse_iterator;

	iterator begin() {
		return _t.Begin();
	}
	const_iterator begin() const {
		return _t.Begin();
	}
	iterator end() {
		return _t.End(); 
	}
	const_iterator end() const {
		return _t.End();
	}

	reverse_iterator rbegin() {
		return _t.RBegin();
	}
	const_reverse_iterator rbegin() const {
		return _t.RBegin();
	}
	reverse_iterator rend() {
		return _t.REnd();
	}
	const_reverse_iterator rend() const {
		return _t.REnd();
	}
	

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

	size_t size() {
		return _t.Size();
	}

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

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

	V& operator[](const K& key) {
		pair<iterator, bool> pr = _t.Insert( make_pair(key, V()));
		iterator pos = pr.first;
		return pos->second;
		
	}
private:
	//不支持修改key,所以将map中的pair中的第一个参数设置为const
	RBTree<K, pair<const K, V>, MapKeyOfT> _t;
};
//set
namespace myspace {
	
	template<class K>
	class set {

		struct SetKeyOfT {
			const K& operator()(const K& key) {
				return key;
			}
		};
	public:

		typedef typename RBTree<K, const K, SetKeyOfT>::Iterator iterator;
		typedef typename RBTree<K, const K, SetKeyOfT>::ConstIterator const_iterator;
		typedef typename RBTree<K, const K, SetKeyOfT>::ReverseIterator reverse_iterator;
		typedef typename RBTree<K, const K, SetKeyOfT>::ConstReverseIterator const_reverse_iterator;

		iterator begin() {
			return _t.Begin();
		}
		const_iterator begin() const {
			return _t.Begin();
		}
		iterator end() {
			return _t.End();
		}
		const_iterator end() const {
			return _t.End();
		}

		reverse_iterator rbegin() {
			return _t.RBegin();
		}
		const_reverse_iterator rbegin() const {
			return _t.RBegin();
		}
		reverse_iterator rend() {
			return _t.REnd();
		}
		const_reverse_iterator rend() const {
			return _t.REnd();
		}

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

		size_t size() {
			return _t.Size();
		}

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

		const_iterator find(const K& key) const {
			return _t.Find(key);     
		}
	private:
		//不允许修改key
		RBTree<K, const K, SetKeyOfT> _t;
	}; 

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

//本项目将用红黑树来实现模拟实现map和set这两个容器
//实现的版本不会带有重复的值(要实现也简单 改一点逻辑就好了)

//底层是使用上一个项目中实现的RBTree,在此基础上进行修改一下

namespace myspace {

	enum color{
		BLACK,
		RED
	};

	//因为map和set底层都是RBTree,但是逻辑上只有一点点差别(比如插入的元素,比较的逻辑等)
	//但是又不想实现两个版本的RBTree,所以需要多次使用模板参数套用

	template<class T>//这个T就是数据的类型,如果是map会传pair类进来 set传的是T
	struct RBTreeNode {
	    T _data;
		color _col;

		RBTreeNode<T>* _left;
		RBTreeNode<T>* _right; 
		RBTreeNode<T>* _parent; 

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



	//迭代器部分
	template<class T, class Ref, class Ptr>
	struct RBTree_Iterator {
		using Node = RBTreeNode<T>;
		using Self = RBTree_Iterator<T, Ref, Ptr>;

		Node* _node;
		Node* _root;
		
		RBTree_Iterator(Node* node, Node* root)
			:_node(node),
			_root(root)
		{}


		Self& operator++() {
			Node* cur = _node;
			if (cur->_right) {
				cur = cur->_right;
				while (cur->_left) {
					cur = cur->_left;
				}
				_node = cur;
			}
			else {
				//右边为空 说明这个子树访问完成
				Node* parent = cur->_parent;
				while (parent && cur == parent->_right) {
					cur = parent;
					parent = parent->_parent;
				}
				//此时有两种情况
				//cur在parent的左边 或者 cur在根节点parent为空
				_node = parent; 
			}
			return *this;
		}

		Self operator++(int) {
			Self it = *this;
			++(*this);
			return it;
		}

		Self& operator--() {
			Node* cur = _node;
			if (_node == nullptr) {
				Node* proot = _root;
				while (_root && _root->_right) {
					_root = _root->_right;
				}
				_node = _root; 
			}
			//有一个问题就是 如果只是将++的逻辑反过来会出问题 
			//从end位置开始走的会出问题
			//因为我们的写法是end位置是空指针
			else if (cur->_left) {
				cur = cur->_left;
				while (cur->_right) {
					cur = cur->_right;    
				}
				_node = cur;
			}
			else {
				//此时左边为空  又因为是倒着走
				//说明此时此时这个子树倒着遍历走完了
				Node* parent = cur->_parent;
				while (parent && cur == parent->_left) {
					cur = parent;
					parent = parent->_parent;
				}
				_node = parent;
			}

			return *this;
		}
		
		Self operator--(int) {
			Self it = *this;
			--(*this);
			return it;
		}

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

		bool operator!=(const Self& self) const{
			return !(*this == self);
		}

		Ref operator*() const{
			assert(_node != nullptr);
			return _node->_data;
		}

		Ptr operator->() const{
			assert(_node != nullptr);
			return &(_node->_data);
		}

	};



	//封装正向迭代器为反向迭代器
	template<class T, class Ref, class Ptr>
	struct RBTRee_ReverseIterator {
		using Node = RBTreeNode<T>;
		using Self = RBTRee_ReverseIterator<T, Ref, Ptr>;

		RBTree_Iterator<T, Ref, Ptr> _it;

		RBTRee_ReverseIterator(Node* node,	Node* root)
			:_it(RBTree_Iterator<T, Ref, Ptr>(node, root))
		{}

		Self& operator++() {
			--_it;
			return *this;
		}

		Self operator++(int) {
			Self it = *this;
			++(*this);
			return it;
		}

		Self& operator--() {
			//为了避免使用反向迭代器倒着走(其实就是正向)
			if (_it._node == nullptr) {
				Node* cur = _it._root;
				while (cur && cur->_left) {
					cur = cur->_left;
				}
				_it._node = cur;
			}
			else ++_it;

			return *this;
		}

		Self operator--(int) {
			Self it = *this;
			--(*this);
			return it;
		}

		bool operator==(const Self& self) const {
			return _it == self._it;
		}

		bool operator!=(const Self& self) const {
			return !(*this == self);
		}

		Ref operator*() {
			return *_it; 
		}
		
		Ptr operator->() {
			return _it.operator->();
		}
	};


	//但是将插入的数据泛化成T之后就会出现一个问题,就是插入比较逻辑会出问题
	//插入是根据key来比较的,但是现在泛化成T怎么办?
	//只能中和一下,多给一个模板参数
	//将T中的key传过来
	template<class K, class T, class KeyOfT>
	class RBTree {
	public:
		using Node = RBTreeNode<T>;    

		using Iterator = RBTree_Iterator<T, T&, T*>;
		using ConstIterator = RBTree_Iterator<T, const T&, const T*>;
		using ReverseIterator = RBTRee_ReverseIterator<T, T&, T*>;
		using ConstReverseIterator = RBTRee_ReverseIterator<T, const T&, const T*>;   

		Node* LeftMost() const {
			Node* cur = _root;
			while (cur && cur->_left) {
				cur = cur->_left;
			}
			return cur;
		}

		Node* RightMost() const {
			Node* cur = _root;
			while (cur && cur->_right) {
				cur = cur->_right;
			}
			return cur;
		}
		
		RBTree() = default;//强制生成默认构造

		RBTree(const RBTree& rb_tree) {
			_root = Copy(rb_tree._root);
			_Node_num = rb_tree._Node_num;
		}

		RBTree<K, T, KeyOfT>& operator=(RBTree<K, T, KeyOfT> tree) {
			std::swap(this->_root, tree._root);
			std::swap(this->_Node_num, tree._Node_num);
			return *this;
		}
		
		~RBTree() {
			Destory(_root);
			_root = nullptr;
		}
		
		Iterator Begin() {
			Node* cur = LeftMost();
			return Iterator(cur,_root);
		}

		Iterator End() {
			return Iterator(nullptr, _root);
		}

		ConstIterator Begin() const {
			Node* cur = LeftMost();
			return ConstIterator(cur, _root); 
		}

		ConstIterator End() const{
			return ConstIterator(nullptr, _root);
		}

		ReverseIterator RBegin() {
			Node* cur = RightMost();
			return ReverseIterator(cur, _root);
		}

		ReverseIterator REnd() {
			return ReverseIterator(nullptr, _root);
		}

		ConstReverseIterator RBegin() const {
			Node* cur = RightMost();
			return ConstReverseIterator(cur, _root);  
		}
		
		ConstReverseIterator REnd() const{
			return ReverseIterator(nullptr, _root);
		}



		//这里相较于之前实现的要稍微改动一下
		pair<Iterator, bool> Insert(const T& data) {
			if (_root == nullptr) {
				_root = new Node(data);  
				_root->_col = BLACK; 
				++_Node_num;
				return { Iterator(_root, _root), true }; 
			}

			//先按照二叉树的逻辑插入
			KeyOfT kot;
			Node* parent = _root, * cur = _root;
			while (cur) {
				if (kot(data) > kot(cur->_data))
					cur = cur->_right;
				else if (kot(data) < kot(cur->_data))
					cur = cur->_left;
				else return { Iterator(cur, _root),false };

				if (cur) parent = cur;
			}
			//此时cur指向被插入的位置 parent指向被插入位置的父亲节点
			cur = new Node(data);
			Node* ret = cur;//记录一下返回值
			if (kot(data) > kot(parent->_data)) parent->_right = cur;    
			else parent->_left = cur;
			cur->_parent = parent;
			++_Node_num;   
  


			//根据红黑树的规则来调整这个树
			//此时cur指向被插入节点 parent指向其父亲节点
			while (parent && parent->_col == RED) {
				Node* grandfather = parent->_parent;

				//  g
				// p u
				// c在p的左边或者右边 
				if (parent == grandfather->_left) {
					Node* uncle = grandfather->_right;

					if (uncle && uncle->_col == RED) {
						//这种情况只需要变色 不断向上调整就可以了(uncle存在且为红)
						parent->_col = uncle->_col = BLACK;
						grandfather->_col = RED;

						//继续向上调整
						cur = grandfather;
						parent = cur->_parent;
					}

					else {
						//叔叔不存在或者存在且为黑 都需要进行旋转 + 变色操作
						//但是需要判断一下是单旋还是双旋
						if (cur == parent->_left) {
							//  g
							// p  u
							//c
							//单旋 + 变色
							RotateR(grandfather);
							parent->_col = BLACK;
							grandfather->_col = RED;

						}
						else {
							//  g
							// p  u
							//  c
							//双旋 + 变色
							RotateL(parent);
							RotateR(grandfather);
							cur->_col = BLACK;
							grandfather->_col = RED;
						}

						break;
					}
				}

				// g
				//u  p
				//c在p的左边或者右边
				else {
					Node* uncle = grandfather->_left;

					if (uncle && uncle->_col == RED) {
						//这种情况只需要变色 不断向上调整就可以了(uncle存在且为红)
						parent->_col = uncle->_col = BLACK;
						grandfather->_col = RED;

						//继续向上调整
						cur = grandfather;
						parent = cur->_parent;
					}

					else {
						//此时叔叔不存在或者存在且为黑色

						if (cur == parent->_right) {
							// g
							//u  p
							//     c
							//此时需要进行单旋 + 变色 
							RotateL(grandfather);
							parent->_col = BLACK;
							grandfather->_col = RED;
						}

						else {
							// g
							//u  p
							//  c
							//双旋 + 变色
							RotateR(parent);
							RotateL(grandfather);
							cur->_col = BLACK;
							grandfather->_col = RED;
						}

						break;
					}

				}
			}

			_root->_col = BLACK;
			return { Iterator(ret, _root), true };

		}

		size_t Size() {
			return _Node_num;
		}

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

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

	private:
		
		void Destory(Node* root){
			if (root == nullptr) return;

			Destory(root->_left);
			Destory(root->_right);
			delete root;

		}

		Node* Copy(Node* root) {
			if (root == nullptr)
				return nullptr;
			
			Node* newRoot = new Node(root->_data);

			newRoot->_left = Copy(root->_left);
			if (newRoot->_left) newRoot->_left->_parent = newRoot;

			newRoot->_right = Copy(root->_right);
			if (newRoot->_right) newRoot->_right->_parent = newRoot;

			return newRoot;
		}

		//旋转代码 有用
		void RotateR(Node* parent) {
			//右单旋其实就是把cur的右孩子给parent作为左孩子
			//再让cur作为根节点 parent作为cur的右孩子
			//还得修改一下被改动节点的_parent指针指向
			//然后需要将新的子树根节点与原来指向此子树的根节点的节点进行链接
			Node* Parent = parent;
			Node* subL = parent->_left;
			Node* subLR = subL->_right;

			Node* P_Parent = Parent->_parent;

			Parent->_left = subLR;
			//subLR可能为空 所以需要判断
			if (subLR != nullptr) subLR->_parent = Parent;

			subL->_right = Parent;
			Parent->_parent = subL;

			//链接原来的AVLTree
			//但是这时候有一个问题 即原来的子树根节点的父亲P_Parent可能是空(对应此时Parent是整个树的根节点)
			if (Parent == _root) {
				_root = subL;
				subL->_parent = nullptr;
			}
			else {
				//此时的新根节点subL不知道是应该插入在P_Parent的左边还是右边 需要判断一下
				if (P_Parent->_left == Parent) P_Parent->_left = subL;
				else P_Parent->_right = subL;

				subL->_parent = P_Parent;
			}

		}

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

			Node* P_Parent = Parent->_parent;

			Parent->_right = subRL;
			if (subRL != nullptr) subRL->_parent = Parent;

			subR->_left = Parent;
			Parent->_parent = subR;

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

				subR->_parent = P_Parent;
			}

		}

		Node* _root = nullptr;
		size_t _Node_num = 0;
	};




}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值