红黑树的实现以及对map和set的封装

目录

一.红黑树的概念

1.1红黑树的规则

1.2红黑树的效率计算

二.红黑树的实现

2.1红黑树的插入

2.1.1变色

2.2红黑树的查找

2.3红黑树的验证

三.封装map和set

3.1底层Iterator的实现

3.1.1Iterator的++和--的逻辑

3.2封装set和map


一.红黑树的概念

红黑树是一颗平衡二叉搜索树,他的每一个结点都有一个存储位来存储表示结点的颜色,红色或者是黑色。通过对于任何一条从根到叶子路径上各个结点的颜色进行约束,红黑树也就确保了任何一条路径不能是最短路径的二倍。

1.1红黑树的规则

1.每一个结点的颜色不是黑色就是红色。

2.根节点是黑色的

3.不能出现连续的红色结点

4.对于任意一条路径,他所有的黑色结点的数量是一样的。--这个规则也极具确定了如果存在黑红这样搭配的那该树的高度==全黑树的二倍,也就保证了他的高度

1.2红黑树的效率计算

因为上述的规则能知道假设红黑树的最短路径是bh(路径上全是黑色结点) --那么最长的结点的高度可能 <=2*bh

假设N是红黑树的结点数量,h是最短路径的长度 2^h - 1 <= N <= 2^2*h - 1,由此能够推出

h = logN那么他的增删查改的时间复杂度就是O(logN)

红黑树区别于AVL树,红黑树通过4个规则来限制整颗树,控制整颗树的高度差,相比于AVL树,红黑树在插入相同数量的结点这一情况下,AVL树会旋转更多次,红黑树的要求没有那么严格

二.红黑树的实现

cpp 复制代码
// 枚举值表⽰颜⾊
enum Colour
{
	RED,
	BLACK
};
// 这⾥我们默认按key/value结构实现
template<class K, class V>
struct RBTreeNode
{
	// 这⾥更新控制平衡也要加⼊parent指针
	pair<K, V> _kv;
	RBTreeNode<K, V>* _left;
	RBTreeNode<K, V>* _right;
	RBTreeNode<K, V>* _parent;
	Colour _col;
	RBTreeNode(const pair<K, V>& kv)
		:_kv(kv)
		, _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
	{
	}
};
template<class K, class V>
class RBTree
{
	typedef RBTreeNode<K, V> Node;
public:
private:
	Node* _root = nullptr;
};

2.1红黑树的插入

之前AVL树的基础我们知道--这里的插入也是大差不差的,但是还是由特殊的问题需要考虑

我们新插入的结点需要将他的颜色弄成红色,如果弄成黑色,那么会违反规则4和弄成红色也可能会出现连续的红色结点这时我们就需要更新一些部分的结点的颜色---根节点要除外(要保证根是黑的)

2.1.1变色

情况1:只变色,不旋转

如果插入结点出现了两个连续的红色结点,我们要去看他的叔叔u,如果u也是红色结点,我们只需要把p和u都变成黑色,并且把g变成红色就解决了这一小部分的问题,但是我们把g给变红色我们还得往上判断是否有连续的红色

情况二:旋转加变色

1.这里是u不存在--c一定就是新插入的结点因为之前没插入的时候u不存在就意味着p没有子树--左单选然后将p变成黑色,g变成红色break;

2.这种情况只能是c是调整上来变成红色的,因为之前没插入前u是黑色,说明p的子树还有一个黑色结点,因为p是红色,所以c之前这个结点时黑色,调整完之后c的子树都有黑色节点了,但是c变成红色,这种情况单边高左单选就可以旋转之后p变成黑色,g变成红色,这样也平衡了

这里还有情况涉及单旋和双旋条件判断因为,上一篇我讲了AVL树他这里我就用最简单的图来展示了,缺失的地方大家补一下

他这个红黑树的判断是u不存在和u是黑色的逻辑是一样的只是两种是不同的情况,但是逻辑代码执行的是一样的,大家把这个图补上u和c的左右和p的右他其实逻辑上也是需要双旋解决问题

这种情况是要双旋的右边高但是是因为p的左边插入了一个结点,所有他是右左双旋,旋转完之后是c变黑,g变红

还有另外一种情况的双旋,我这里画一个图就行

这个c就是下面调整变色变称红色的,其实逻辑也是一样p先左旋,g在右旋,旋转完之后把c变成黑色

不存在和u是黑色可以一块写

cpp 复制代码
pair<Iterator,bool> Insert(const T& data)
{
	if (_root == nullptr)
	{
		_root = new Node(data);
		_root->_col = BLACK;
		return { Iterator(_root,_root),true };
	}
	KeyOfT kot;
	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 { Iterator(cur,_root),false };
		}
	}
	cur = new Node(data);
	Node* newnode = cur;
	cur->_col = RED;
	if (kot(parent->_data) > kot(data))
		parent->_left = cur;
	else
		parent->_right = cur;
	cur->_parent = parent;


	//插入完毕,检查红黑树规则是否满足
	while (parent && parent->_col == RED)
	{
		Node* grandparent = parent->_parent;
		if (grandparent->_left == parent)
		{
			Node* uncle = grandparent->_right;
			if (uncle && uncle->_col == RED)
			{
				parent->_col = BLACK;
				uncle->_col = BLACK;
				grandparent->_col = RED;
				cur = grandparent;
				parent = cur->_parent;
			}
			else
			{
				//uncle不存在或者他的颜色是红色
				//判断cur和parent的关系再进行旋转加上变色
				if (parent->_left == cur)
				{
					//右旋--旋转之后停下来
					RotateR(grandparent);
					parent->_col = BLACK;
					grandparent->_col = RED;
				}
				else
				{
					RotateL(parent);
					RotateR(grandparent);
					cur->_col = BLACK;
					grandparent->_col = RED;
				}
				break;
			}
		}
		//grandparent->_right == parent
		else
		{
			Node* uncle = grandparent->_left;
			if (uncle && uncle->_col == RED)
			{
				grandparent->_col = RED;
				parent->_col = uncle->_col = BLACK;
				cur = grandparent;
				parent = cur->_parent;
			}
			else
			{
				//uncle不存在或者是黑色
				if (parent->_right == cur)
				{
					RotateL(grandparent);
					grandparent->_col = RED;
					parent->_col = BLACK;
				}
				else
				{
					RotateR(parent);
					RotateL(grandparent);
					grandparent->_col = RED;
					cur->_col = BLACK;
				}
				break;
			}
		}
	}
	_root->_col = BLACK;
	return { Iterator(newnode,_root),true };
}
void RotateR(Node* cur)
{
	Node* subL = cur->_left;
	Node* subLR = subL->_right;
	Node* parent = cur->_parent;

	cur->_left = subLR;
	if (subLR)
		subLR->_parent = cur;
	cur->_parent = subL;
	subL->_right = cur;
	
	subL->_parent = parent;
	if (parent == nullptr)
	{
		_root = subL;
	}
	else
	{
		if (parent->_left == cur)
		{
			parent->_left = subL;
		}
		else
		{
			parent->_right = subL;
		}
	}
}
void RotateL(Node* cur)
{
	Node* parent = cur->_parent;
	Node* subR = cur->_right;
	Node* subRL = subR->_left;

	cur->_right = subRL;
	if (subRL)
	{
		subRL->_parent = cur;
	}
	subR->_left = cur;
	cur->_parent = subR;

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

插入写完之后其余的也就没什么了,这个文档插入我写的逻辑性很差,大家可以多看看理解一下,旋转的逻辑,--我觉得关键是为什么u不存在和u是黑色能一起写代码

2.2红黑树的查找

红黑树的查找根二叉搜索树的查找一样的

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

2.3红黑树的验证

验证逻辑也就是看一看规则3和规则4比较的重要,但是我们每次去查看他的孩子结点还要判断它的孩子是否存在我们其实可以反过来,让孩子去查看它父亲结点是不是也是红色的,我们还需要知道一个树走到最后右多少个黑色结点,需要我们走这个树

也就是走到空要判断这一路上的黑色结点的数量是不是根某条路径上黑的数量相同,我们将这个数量作为参数传到函数中,每次判断即可

cpp 复制代码
void _InOrder(Node* root)
{
	if (root == nullptr)
		return;
	_InOrder(root->_left);
	cout << root->_kv.first << ":" << root->_kv.second << endl;
	_InOrder(root->_right);
}
//前序遍历
bool Check(Node* root, int blackNum, const int refNum)
{
	if (root == nullptr)
	{
		if (refNum != blackNum)
		{
			cout << "存在黑色结点数量不相等的路径" << endl;
			return false;
		}
		return true;
	}
	//检查父亲结点是不是红色的
	if (root->_col == RED && root->_parent && root->_parent->_col == RED)
	{
		cout << "存在连续的红色结点" << endl;
		return false;
	}
	if (root->_col == BLACK)
	{
		blackNum++;
	}
	return Check(root->_left, blackNum, refNum) && Check(root->_right, blackNum, refNum);
}
bool IsBalance()
{
	if (_root == nullptr)
	{
		return true;
	}
	if (_root->_col == RED)
	{
		return false;
	}
	int refNum = 0;
	Node* cur = _root;
	while (cur)
	{
		if (cur->_col == BLACK)
		{
			refNum++;
		}
		cur = cur->_left;
	}
	return Check(_root, 0, refNum);
}

三.封装map和set

我们知道在c++中map和set的底层都是用红黑树来实现的---我们也在这个地方来封装一下我们自己的map和set

3.1底层Iterator的实现

在底层我们设计出Iterator来,我们先不管逻辑是怎么样的我们先看使用,在库里面map和set的迭代器是双向的迭代器,所以底层也需要设计出这样的迭代器,因为我们设计的树是通过left,right和parent联系在一起的,所以我们需要包装一下iterator(像之前实现链表的迭代器一样)

我们的Iterator底层使用has-a来来实现,我们在Iterator的底层存一个RBTree的指针cur,和_root来辅助我们的操作,另外,我们为了实现泛型编程我们将const和普通迭代器都能用这一个类实例化我们借鉴链表迭代器的思想,新增两个模板参数用来标识const和普通的引用与指针。

3.1.1Iterator的++和--的逻辑

因为我们是中序遍历的情况是有序的,所以我们++和--也走类似中序遍历,但是中序遍历是便利的整颗树,我们++和--不需要走完,所以我们了解一下,看着实际情况走一下++的逻辑

++如果它有右子树,就让它走右边,去找右边树的最左结点,它的右子树为空了,说明该结点结点走完了,下一个结点是把结点当成左边孩子的结点,如果找到了它就是结点,如果没有找到,说明父亲结点也走完了,让它向上去找。

--的逻辑如果他又左结点,就去找左边最大的结点(最右节点),如果左边是空的,就需要找一个结点把它当成右孩子,下一个结点就是他,如果不是,那就他的父亲去找。特殊的,如果--的一开始是nullptr,需要我们去找最右结点

cpp 复制代码
template<class T,class Ref,class Ptr>
struct RBTreeIterator
{
	typedef RBTreeNode<T> Node;
	typedef RBTreeIterator<T, Ref, Ptr> Self;

	Node* _node;
	Node* _root;
	RBTreeIterator(Node* node, Node* root)
		:_node(node)
		, _root(root)
	{
	}
	Self& operator++()
	{
		if (_node->_right)
		{
			Node* leftMost = _node->_right;
			while (leftMost->_left)
			{
				leftMost = leftMost->_left;
			}
			_node = leftMost;
		}
		else
		{
			//孩纸是父亲的左
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && parent->_right == cur)
			{
				cur = parent;
				parent = cur->_parent;
			}
			_node = parent;
		}
		return *this;
	}
	Self& operator--()
	{
		if (_node == nullptr)
		{
			Node* rightMost = _root;
			while (rightMost && rightMost->_right)
			{
				rightMost = rightMost->_right;
			}
			_node = rightMost;
		}
		else if (_node->_left)
		{
			Node* rightMost = _node->_left;
			while (rightMost->_right)
			{
				rightMost = rightMost->_right;
			}
			_node = rightMost;
		}
		else
		{
			Node* cur = _node;
			Node* parent = cur->_parent;

			while (parent && parent->_left == cur)
			{
				cur = parent;
				parent = cur->_parent;
			}
			_node = parent;
		}
		return *this;
	}
	Ref operator*()
	{
		return _node->_data;
	}
	Ptr operator->()
	{
		return &_node->_data;
	}
	bool operator!=(const Self& s)const
	{
		return _node != s._node;
	}
	bool operator==(const Self& s)const
	{
		return _node == s._node;
	}
};

封装完了,在底层RBTree就把他的底层加上有关迭代器相关的成员函数,在加上销毁结点的析构函数,这个底层RBTree就大致完成了

cpp 复制代码
template<class K,class T,class KeyOfT>
class RBTree
{
public:
	typedef RBTreeNode<T> Node;
	typedef RBTreeIterator<T, T&, T*> Iterator;
	typedef RBTreeIterator<T, const T&, const T*> ConstIterator;
	RBTree() = default;

	~RBTree()
	{
		Destroy(_root);
		_root = nullptr;
	}
	void Destroy(Node* root)
	{
		if(root == nullptr)
			return;
		
		//后续遍历
		Destroy(root->_left);
		Destroy(root->_right);

		delete root;

	}
	Iterator Begin()
	{
		Node* leftMost = _root;
		while (leftMost && leftMost->_left)
		{
			leftMost = leftMost->_left;
		}
		return Iterator(leftMost, _root);
	}
	Iterator End()
	{
		return Iterator(nullptr, _root);
	}
	ConstIterator Begin()const
	{
		Node* leftMost = _root;
		while (leftMost && leftMost->_left)
		{
			leftMost = leftMost->_left;
		}
		return ConstIterator(leftMost, _root);
	}
	ConstIterator End()const
	{
		return ConstIterator(nullptr, _root);
	}

	pair<Iterator,bool> Insert(const T& data)
	{
		if (_root == nullptr)
		{
			_root = new Node(data);
			_root->_col = BLACK;
			return { Iterator(_root,_root),true };
		}
		KeyOfT kot;
		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 { Iterator(cur,_root),false };
			}
		}
		cur = new Node(data);
		Node* newnode = cur;
		cur->_col = RED;
		if (kot(parent->_data) > kot(data))
			parent->_left = cur;
		else
			parent->_right = cur;
		cur->_parent = parent;


		//插入完毕,检查红黑树规则是否满足
		while (parent && parent->_col == RED)
		{
			Node* grandparent = parent->_parent;
			if (grandparent->_left == parent)
			{
				Node* uncle = grandparent->_right;
				if (uncle && uncle->_col == RED)
				{
					parent->_col = BLACK;
					uncle->_col = BLACK;
					grandparent->_col = RED;
					cur = grandparent;
					parent = cur->_parent;
				}
				else
				{
					//uncle不存在或者他的颜色是红色
					//判断cur和parent的关系再进行旋转加上变色
					if (parent->_left == cur)
					{
						//右旋--旋转之后停下来
						RotateR(grandparent);
						parent->_col = BLACK;
						grandparent->_col = RED;
					}
					else
					{
						RotateL(parent);
						RotateR(grandparent);
						cur->_col = BLACK;
						grandparent->_col = RED;
					}
					break;
				}
			}
			//grandparent->_right == parent
			else
			{
				Node* uncle = grandparent->_left;
				if (uncle && uncle->_col == RED)
				{
					grandparent->_col = RED;
					parent->_col = uncle->_col = BLACK;
					cur = grandparent;
					parent = cur->_parent;
				}
				else
				{
					//uncle不存在或者是黑色
					if (parent->_right == cur)
					{
						RotateL(grandparent);
						grandparent->_col = RED;
						parent->_col = BLACK;
					}
					else
					{
						RotateR(parent);
						RotateL(grandparent);
						grandparent->_col = RED;
						cur->_col = BLACK;
					}
					break;
				}
			}
		}
		_root->_col = BLACK;
		return { Iterator(newnode,_root),true };
	}
	Iterator Find(const K&key)
	{
		Node* cur = _root;
		KeyOfT kot;
		while (cur)
		{
			if (kot(cur->_data) < key)
			{
				cur = cur->_right;
			}
			else if (kot(cur->_data) > key)
			{
				cur = cur->_left;
			}
			else
			{
				return { cur,_root };
			}
		}
		return End();
	}
	void RotateR(Node* cur)
	{
		Node* subL = cur->_left;
		Node* subLR = subL->_right;
		Node* parent = cur->_parent;

		cur->_left = subLR;
		if (subLR)
			subLR->_parent = cur;
		cur->_parent = subL;
		subL->_right = cur;
		
		subL->_parent = parent;
		if (parent == nullptr)
		{
			_root = subL;
		}
		else
		{
			if (parent->_left == cur)
			{
				parent->_left = subL;
			}
			else
			{
				parent->_right = subL;
			}
		}
	}
	void RotateL(Node* cur)
	{
		Node* parent = cur->_parent;
		Node* subR = cur->_right;
		Node* subRL = subR->_left;

		cur->_right = subRL;
		if (subRL)
		{
			subRL->_parent = cur;
		}
		subR->_left = cur;
		cur->_parent = subR;

		subR->_parent = parent;
		if (parent == nullptr)
		{
			_root = subR;
		}
		else
		{
			if (parent->_left == cur)
			{
				parent->_left = subR;
			}
			else
			{
				parent->_right = subR;
			}
		}
	}
private:
	Node* _root = nullptr;
};

3.2封装set和map

因为我们像用一个底层去构建map和set因此我们这个地方就不能简单使用pair<>类型当作Node的data类型。用T去左data的类型,这样不管是key还是key-value的类型我们都能使用,上层用KeyOfT这个仿函数取T类型中的key值,这样我们RBTree的模板就可以实例化出来两个类,分别标识set和map。有这个仿函数是为了能从T的data中取出k值进行可以比较,没有这个的话我们不太方便区分map和set的key值 --上层定义的仿函数给下层使用。

我们使用has-a的方法实现set和map 因为我们的key值不管是set还是map都是不允许修改的所以这个地方我们存的可以是const k类型的对象

我们迭代器实现是依靠下层的迭代器的,我们需要在下一层的类中取出来迭代器,但是注意模板参数要根对象保持一致,这样才能准确取出想要的迭代器类型。但是在模板里面取迭代器必须要展示他是类型,不是全局函数加上typename,上层的大部分函数都是调用的底层的函数,我们需要改造一下insert把他的返回值弄成pair<iterator,bool>这样方便map支持重载\[\]。重载的\[\]逻辑也是insert插入key-value的pair如果存在bool是假,如果不存在bool是真

cpp 复制代码
//set.h
namespace wyh
{
	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;
		pair<iterator,bool>insert(const K& key)
		{
			return _t.Insert(key);
		}

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

		iterator find(const K& key)
		{
			return _t.Find(key);
		}
		void Print(const set<int>& s)
		{
			set<int>::const_iterator it = s.end();
			while (it != s.begin())
			{
				--it;
				// 不⽀持修改
				//*it += 2;
				cout << *it << " ";
			}
			cout << endl;
		}

		void test_set()
		{
			set<int> s;
			int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
			for (auto e : a)
			{
				s.insert(e);
			}
			for (auto e : s)
			{
				cout << e << " ";
			}
			cout << endl;
			Print(s);
		}

	private:
		RBTree<K, const K, SetKeyOfT> _t;
	};
}
cpp 复制代码
//map.h
namespace wyh
{
	template<class K,class V>
	class map
	{
		struct MapKeyOfT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};
	public:
		//这个地方的类型要和底层的_t是一样的参数要不然实例化出的不是同一个类也就不能调用_t的函数
		typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::Iterator iterator;
		typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::ConstIterator const_iterator;

		iterator begin()
		{
			return _t.Begin();
		}
		iterator end()
		{
			return _t.End();
		}
		const_iterator begin()const
		{
			_t.Begin();
		}
		const_iterator end()const
		{
			_t.End();
		}
		iterator find(const K& key)
		{
			return _t.Find(key);
		}
		V& operator[](const K& key)
		{
			pair<iterator, bool> ret = insert(make_pair(key, V()));
			return ret.first->second;
		}
		pair<iterator,bool> insert(const pair<K, V>& kv)
		{
			return _t.Insert(kv);
		}
		void test_map()
		{
			map<string, string> dict;
			dict.insert({ "sort", "排序" });
			dict.insert({ "left", "左边" });
			dict.insert({ "right", "右边" });
			dict["left"] = "左边,剩余";
			dict["insert"] = "插入";
			dict["string"];
			map<string, string>::iterator it = dict.begin();
			while (it != dict.end())
			{
				// 不能修改first,可以修改second
				//it->first += 'x';
				it->second += 'x';
				cout << it->first << ":" << it->second << endl;
				++it;
			}
			cout << endl;
		}
	private:
		RBTree<K, pair<const K, V>, MapKeyOfT> _t;
	};

}
相关推荐
骑着蜗牛撵大象3271 小时前
从三次握手到 epoll:C++ 写出可落地的 TCP Socket 服务端
网络·c++·tcp/ip·高并发·tcp·epoll
卡卡~~1 小时前
C++学习
开发语言·c++
杜 硕2 小时前
从零开始的C++生活——第四讲
开发语言·c++·生活
努力实干3 小时前
Qt/C++ 模块化设计与工程组织
开发语言·c++·qt
Rabitebla3 小时前
【Linux底层原理】 工具篇(二):Vim 编辑器基础操作与简单配置
linux·c++·编辑器·vim
Lhan.zzZ3 小时前
Qt:动态库模块框架设计
开发语言·c++·qt
wuminyu3 小时前
JVM规范学习总结-JVM规范基本概念
java·linux·c语言·jvm·c++
Demon--hx4 小时前
jsoncpp 核心概念梳理:命名空间、Value 容器与格式化输出
c++
syagain_zsx16 小时前
BRpc 二次封装:Channel 管理、服务管理与回调工厂
c++·学习·程序人生·rpc·谷歌