基于红黑树对map和set的封装

前言

前面我们已经对红黑树做了介绍和实现,本期我们来对红黑树进一步改造,然后基于改造后的红黑树封装出map和set!

本期内容介绍

• 红黑树的改造

• 红黑树的迭代器实现

• map的封装

• set的封装

• 全部源码

● 红黑树的改造

我们目前的实现的红黑树,里面存的是一个pair<K,V>的键值对,但是我们知道set是只有key没有val的,map是<key,val>的!而他两都是基于红黑树实现的,难道是弄两棵红黑树分别封装?这是不是太cuo了呀!显然不是的,库里面他两底层用的是一颗红黑树:

这里人家是将底层存的数据没有写死,给了一个Value的类型!你上层如果封装成map就给pair<K,V>,如果是封装成set就给K即可!(后面的比较器和空间配置器暂时就不看了)

• 问题一:set使用时是一个key为什么底层也要传给红黑树两个key?

由于map和set的底层都用的是同一棵红黑树,而map存的是K,V类型的键值对,所以这里可以认为是set对map的迁就!所以set即使用的时候只是一个key,但底层还是给红黑树一样的V和K,目的是使得红黑树的模板参数的一致性!

• 问题二:红黑树的是在左插入等操作时,如何知道你是pair还是key呢?

的确红黑树那一层是不知道的,但是我们map和set层是知道的!如何让红黑树那一层知道呢?就是第三个参数,是一个获取存储值key的类;当map和set传过去之后,红黑树层可以创建对象获取!

OK,我们也改造一下自己的红黑树出来吧:(我们就把存储的数据类型改成T避免与V混淆)

节点类要就不在是K,V了,而是T了:

insert也是不再是插入(inert后面介绍到[]还会改造

OK,我们先把红黑树的构造和拷贝构造以及赋值拷贝给整出来,在上层例如set进行赋值,拷贝等操作,就会到红黑树这一层,如果红黑树没有就成了浅拷贝了!

• 默认构造

这里本来可以不用写的,但是后面如果实现了赋值拷贝后就自动生不成了,所以得写!

RBTree()
	:_root(nullptr)
{}

还有一种就是即使有了拷贝构造可以强制让编译器生成:

RBTree() = default;//强制让编译器生成默认构造

• 拷贝构造

这里采用二叉树的前序逐一拷贝即可:

RBTree(const RBTree<K, T, KofT>& t)
{
	_root = Copy(t._root);
}
Node* Copy(Node* root)
{
	if (root == nullptr)
		return nullptr;
    
    //复制节点和颜色
	Node* newnode = new Node(root->_data);
	newnode->_col = root->_col;
    
    //复制左孩子
	newnode->_left = Copy(root->_left);
	if (newnode->_left)//连接
		newnode->_left->_parent = newnode;
        
    //复制右孩子
	newnode->_right = Copy(root->_right);
	if (newnode->_right)//连接
		newnode->_right->_parent = newnode;

	return newnode;
}

• 赋值拷贝

直接采用以前的那种现在写法:将形参用值接受,然后与当前交换

RBTree<K, T, KofT>& operator=(RBTree<K, T, KofT> t)
{
	swap(_root, t._root);
}

• 析构函数

采用二叉树的后序先删除做孩子,再删除右孩子,最后删除根节点

~RBTree()
{
	Destory(_root);
	_root = nullptr;
}
void Destory(Node* root)
{
	if (root == nullptr)
		return;

	Destory(root->_left);
	Destory(root->_right);
	delete root;
	root = nullptr;
}

● 红黑树的迭代器实现

红黑树的迭代器和链表的一样,空间不是连续的无法用原生指针来实现,得改一个类来达到“连续”的行为!我们使用的迭代器无非就begin、end、!=, *,->,++等但是begin和end只有红黑树知道,所以我们在迭代器里面只需要实现!=, *, ->,++等即可:

迭代器本质就是也即是一个节点的指针,所以这里的迭代器类和链表的一模一样:

template<class T, class Ref, class Ptr>
struct _RBTree_Iterator
{
	typedef RBTreeNode<T> Node;
	typedef _RBTree_Iterator<T, Ref, Ptr> Self;

	Node* _node;

	_RBTree_Iterator(Node* node)
		:_node(node)
	{}
}

operator*

直接返回该节点的数据的引用即可!

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

operator->

直接返回当前节点的数据的地址(指针)即可!

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

operator !=

直接比较两个节点的地址是否不相等即可!

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

operator==

直接比较两个节点的地址是否相同即可!

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

operator++

实现思路:因为红黑树迭代器走的是中序,即 左->根->右!而*的时候已经访问了该节点的左和他本身++是去找当前节点的下一个节点!所以此时只需要考虑,当前节点的有节点是是否为空!

1、如果当前节点的右节点不为空,下一个要访问的节点就是,右子树的最左节点!

2、如果当前节点的右节点为空,说当树全部都访问完了,此时就得向上找当前节点是父节点的左的父节点!下一个访问的就是这个父节点!

Self& operator++()
{
	if (_node->_right)
	{
		Node* leftMin = _node->_right;
		while (leftMin->_left)
		{
			leftMin = leftMin->_left;
		}

		_node = leftMin;
	}
	else
	{
		Node* cur = _node;
		Node* parent = cur->_parent;
		while (parent && cur == parent->_right)
		{
			cur = parent;
			parent = parent->_parent;
		}

		_node = parent;
	}

	return *this;
}

operator++(int)

有了前置的++后置直接复用即可!后置++的特点是返回+1前的值,这里同理!我们先拷贝一份当前的迭代器,然后当前迭代器复用前置++到下一个要访问的节点,然后返回拷贝即可!

//后置++
Self operator++(int)
{
	Self tmp = *this;
	++(*this);
	return tmp;
}

operator--

前置++的思路是更具中序的:左->根->右!这里的--就和前置++相反:右->根->左!

1、当前节点的左不为空,下一个访问的节点就是当前节点左子树的最右节点!

2、当前节点的左为空,找当前节点是父节点的有孩子的父节点,下一个访问的是该父节点

// 前置--
Self& operator--()
{
	if (_node->_left)
	{
		Node* cur = _node->_left;
		while (cur->_right)
		{
			cur = cur->_right;
		}
		_node = cur;
	}
	else
	{
		Node* cur = _node, * parent = cur->_parent;
		while (parent && parent->_left == cur)
		{
			cur = parent;
			parent = parent->_parent;
		}
		_node = parent;
	}
	return *this;
}

operator--(int)

后置--同理直接先拷贝一份当前节点,然后调用前置--带下一个访问的节点,最后返回该拷贝即可!

//后置--
Self operator--(int)
{
	Self tmp = *this;
	--(*this);
	return tmp;
}

begin

因为迭代器是按照中序走的,所以begin是整棵树的最左节点!

Iterator Begin()
{
	Node* leftMin = _root;
	while (leftMin && leftMin->_left)
	{
		leftMin = leftMin->_left;
	}

	return Iterator(leftMin);
}

end

这里的end应该是最右节点的下一个节点,这里简单处理为nullptr;

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

const_begin

ConstIterator Begin() const
{
	Node* leftMin = _root;
	while (leftMin && leftMin->_left)
	{
		leftMin = leftMin->_left;
	}

	return ConstIterator(leftMin);
}

const_end

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

 ● map的封装

OK,先搞一个框架出来:

#pragma once

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

	private:
		RBTree<K, pair<const K, V>, MapKeyOfT> _m;//map的key不允许修改
	};
}

• 注意:map的key是不允许修改的所以用const修饰

OK,先来整迭代器出来,直接调用干红黑树的即可:

iterator

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 _m.Begin();
}

iterator end()
{
	return _m.End();
}

const_iterator begin() const 
{
	return _m.Begin();
}

const_iterator end() const
{
	return _m.End();
}

注意:

typedef typename RBTree<K, const K, SetKofT>::Iterator iterator;
typedef typename RBTree<K, const K, SetKofT>::ConstIterator const_iterator;

这里的RBTree<K, const K, SetKofT>只是类型不是实体,所以编译器不认识,得加上typename告诉编译器这是类型;

find


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

insert

这里的insert,当前的红黑树是不要满足的,因为我们以前介绍使用的时候介绍过它的返回值是一个pair<iterator, bool>,如果插入成功返回当前插入节点的迭代器,bool设置为true;如果插入的元素失败,返回当前存在元素的迭代器,bool是false;

所以,我么先得改造红黑树的insert:挂在燥起来也不难,就是将返回值换成pair<iterator, bool>即可,唯一注意的一个点就是最后返回的那个节点的迭代器;一定要在前面提前记录变色前的那个新节点的指针,方便后面构造迭代器;如果不记录后面直接返回cur这个cur就不一定是前面的cur了因为会旋转 + 变色改变!!!

pair<Iterator, bool> Insert(const T& data)
{
	//第一次插入
	if (_root == nullptr)
	{
		_root = new Node(data);
		_root->_col = BLACK;//根节点是黑色
		return make_pair(Iterator(_root), true);
	}

	Node* cur = _root;//当前节点
	Node* parent = nullptr;//当前节点的父节点
	KofT kot;
	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);//插入的值已经存在
		}
	}

	//找到了插入位置
	cur = new Node(data);
	Node* newnode = cur;//提前保存返回的元素的位置,方便构造迭代器
	//链接
	if (kot(cur->_data) > kot(parent->_data))
	{
		parent->_right = cur;
	}
	else
	{
		parent->_left = 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)
			{
				grandfather->_col = RED;
				parent->_col = uncle->_col = BLACK;
				//继续更新
				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;//旋转+变色完了就结束掉
			}
		}
		else
		{
			Node* uncle = grandfather->_left;//叔叔在爷爷的左
			//叔叔存在且为红 -> 变色
			if (uncle && uncle->_col == RED)
			{
				grandfather->_col = RED;
				parent->_col = uncle->_col = BLACK;
				//继续更新
				cur = grandfather;
				parent = cur->_parent;
			}
			else//叔叔不存在或为黑 --> 旋转+变色
			{
				if (cur == parent->_left)
				{
					//     g
					//   u   p
					//     c
					RotateR(parent);
					RotateL(grandfather);
					cur->_col = BLACK;
					grandfather->_col = RED;
				}
				else
				{
					//     g
					//   u   p
					//         c
					RotateL(grandfather);
					parent->_col = BLACK;//变色
					grandfather->_col = RED;
				}

				break;//旋转+变色完了就结束掉
			}
		}
	}

	_root->_col = BLACK;//保证根节点永远是黑色
	return make_pair(Iterator(newnode), true);
}

成功改造完红黑的的insert之后map和set的就直接可以调用了!

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

operator[]

[]是基于insert实现的,在介绍使用的时候介绍过,[]按照K值插入,不管插入成功与否,都返回V的引用~!

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

OK,测试一下:

void Print_map(const cp::map<string, int> m)
{
	for (auto& e : m)
	{
		cout << e.first << " : " << e.second << endl;
	}
}

void TestMap()
{
	cp::map<int, int> m1;
	m1.insert({ 1,1 });
	m1.insert({ 2,2 });
	m1.insert({ 3,3 });
	m1.insert({ 4,4 });
	m1.insert({ 5,5 });
	cp::map<int, int>::iterator it = m1.begin();
	while (it != m1.end())
	{
		cout << it->first << ":" << it->second << endl;
		it++;
	}
	cout << "--------------------" << endl;


	cp::map<string, int> m2;
	string s[] = { "aaa", "bbb", "ccc", "bbb", "cpdd", "000", "苹果", "西瓜", "aaa" };
	for (auto& e : s)
		m2[e]++;
	Print_map(m2);
}

● set的封装

同理,我们可以搭建出一个set的基本框架出来:

#pragma once

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

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

OK,先来整迭代器出来,直接调用干红黑树的即可:

inerator

typedef typename RBTree<K, const K, SetKofT>::Iterator iterator;
typedef typename RBTree<K, const K, SetKofT>::ConstIterator const_iterator;

iterator begin()
{
	return _s.Begin();
}

iterator end()
{
	return _s.End();
}

const_iterator begin() const
{
	return _s.Begin();
}

const_iterator end() const
{
	return _s.End();
}

find

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

insert

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

OK,测试一下:

void Print_set(const cp::set<int>& s)
{
	for (auto& e : s)
	{
		cout << e << endl;
	}
}

void TestSet()
{
	vector<int> v = { 8, 3, 1, 10, 6, 4, 7, 14, 13, 8, 6 };
	cp::set<int> s;
	for (auto& e : v)
	{
		s.insert(e);
	}

	Print_set(s);
}

OK,到这里map和set的封装就已经实现完了!一些接口例如size、clear等前面实现过很多次这里就不在实现了!这里想说的是map和set就是套了一层红黑树的壳,真正的核心还是红黑!

● 全部源码

RBTree.h

#pragma once

#pragma once

enum Col
{
	RED = 0,
	BLACK
};

template <class T>
struct RBTreeNode
{
	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right;
	RBTreeNode<T>* _parent;
	T _data;
	Col _col;

	RBTreeNode(const T& data)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _data(data)
		, _col(RED)//默认新的节点是红色
	{}
};

template<class T, class Ref, class Ptr>
struct _RBTree_Iterator
{
	typedef RBTreeNode<T> Node;
	typedef _RBTree_Iterator<T, Ref, Ptr> Self;

	Node* _node;

	_RBTree_Iterator(Node* node)
		:_node(node)
	{}

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

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

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

	//前置++
	Self& operator++()
	{
		if (_node->_right)
		{
			Node* leftMin = _node->_right;
			while (leftMin->_left)
			{
				leftMin = leftMin->_left;
			}

			_node = leftMin;
		}
		else
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_right)
			{
				cur = parent;
				parent = parent->_parent;
			}

			_node = parent;
		}

		return *this;
	}

	//后置++
	Self operator++(int)
	{
		Self tmp = *this;
		++(*this);
		return tmp;
	}

	// 前置--
	Self& operator--()
	{
		if (_node->_left)
		{
			Node* cur = _node->_left;
			while (cur->_right)
			{
				cur = cur->_right;
			}
			_node = cur;
		}
		else
		{
			Node* cur = _node, * parent = cur->_parent;
			while (parent && parent->_right != cur)
			{
				cur = parent;
				parent = parent->_parent;
			}
			_node = parent;
		}
		return *this;
	}

	//后置--
	Self operator--(int)
	{
		Self tmp = *this;
		--(*this);
		return tmp;
	}
};

template <class K, class T, class KofT>
class RBTree
{
	typedef RBTreeNode<T> Node;
public:
	typedef _RBTree_Iterator<T, T&, T*> Iterator;
	typedef _RBTree_Iterator<T, const T&, const T*> ConstIterator;

	RBTree() = default;//强制让编译器生成默认构造

	//RBTree()
	//	:_root(nullptr)
	//{}

	RBTree(const RBTree<K, T, KofT>& t)
	{
		_root = Copy(t._root);
	}

	RBTree<K, T, KofT>& operator=(RBTree<K, T, KofT> t)
	{
		swap(_root, t._root);
	}

	~RBTree()
	{
		Destory(_root);
		_root = nullptr;
	}

	Iterator Begin()
	{
		Node* leftMin = _root;
		while (leftMin && leftMin->_left)
		{
			leftMin = leftMin->_left;
		}

		return Iterator(leftMin);
	}

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

	ConstIterator Begin() const
	{
		Node* leftMin = _root;
		while (leftMin && leftMin->_left)
		{
			leftMin = leftMin->_left;
		}

		return ConstIterator(leftMin);
	}

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

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

		return End();
	}

	pair<Iterator, bool> Insert(const T& data)
	{
		//第一次插入
		if (_root == nullptr)
		{
			_root = new Node(data);
			_root->_col = BLACK;//根节点是黑色
			return make_pair(Iterator(_root), true);
		}

		Node* cur = _root;//当前节点
		Node* parent = nullptr;//当前节点的父节点
		KofT kot;
		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);//插入的值已经存在
			}
		}

		//找到了插入位置
		cur = new Node(data);
		Node* newnode = cur;//提前保存返回的元素的位置,方便构造迭代器
		//链接
		if (kot(cur->_data) > kot(parent->_data))
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = 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)
				{
					grandfather->_col = RED;
					parent->_col = uncle->_col = BLACK;
					//继续更新
					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;//旋转+变色完了就结束掉
				}
			}
			else
			{
				Node* uncle = grandfather->_left;//叔叔在爷爷的左
				//叔叔存在且为红 -> 变色
				if (uncle && uncle->_col == RED)
				{
					grandfather->_col = RED;
					parent->_col = uncle->_col = BLACK;
					//继续更新
					cur = grandfather;
					parent = cur->_parent;
				}
				else//叔叔不存在或为黑 --> 旋转+变色
				{
					if (cur == parent->_left)
					{
						//     g
						//   u   p
						//     c
						RotateR(parent);
						RotateL(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
						//     g
						//   u   p
						//         c
						RotateL(grandfather);
						parent->_col = BLACK;//变色
						grandfather->_col = RED;
					}

					break;//旋转+变色完了就结束掉
				}
			}
		}

		_root->_col = BLACK;//保证根节点永远是黑色
		return make_pair(Iterator(newnode), true);
	}

	void InOrder()
	{
		return _InOrder(_root);
	}

	bool IsBalance()
	{
		if (_root && _root->_col == RED)
			return false;//根节点不可能为红

		int black = 0;//根节点到任意一条从根节点到叶子节点的黑色节点的数目
		Node* cur = _root;
		while (cur)
		{
			if (cur->_col == BLACK)
				black++;

			cur = cur->_left;
		}

		return Check(_root, black, 0);
	}

private:

	Node* Copy(Node* root)
	{
		if (root == nullptr)
			return nullptr;

		Node* newnode = new Node(root->_data);
		newnode->_col = root->_col;

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

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

		return newnode;
	}

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

		Destory(root->_left);
		Destory(root->_right);
		delete root;
		root = nullptr;
	}

	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, const int black, int num)
	{
		if (root == nullptr)
		{
			if (num != black)
				return false;

			return true;
		}

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

		if (root->_col == BLACK)
		{
			++num;
		}

		return Check(root->_left, black, num) && Check(root->_right, black, num);
	}

	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;//父亲的左
		Node* subLR = subL->_right;//左子树的右
		Node* ppNode = parent->_parent;//parent的父节点,方便旋转后的链接

		parent->_left = subLR;//将左子树的右给父亲的做
		if (subLR)
			subLR->_parent = parent;

		subL->_right = parent;//parent做左子树的右
		parent->_parent = subL;

		if (parent == _root)//parent是根
		{
			_root = subL;//此时的新根就是subL
			_root->_parent = ppNode;
		}
		else//parent不是根
		{
			//将新的根连接到ppNode
			if (ppNode->_left == parent)
			{
				ppNode->_left = subL;
			}
			else
			{
				ppNode->_right = subL;
			}
			subL->_parent = ppNode;
		}
	}

	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;//父亲的右
		Node* subRL = subR->_left;//右子树的左
		Node* ppNode = parent->_parent;//parent的父节点,方便旋转后的链接

		parent->_right = subRL;//将右子树的左连接到parent的右
		if (subRL)
			subRL->_parent = parent;

		subR->_left = parent;//parent连接到subR的左
		parent->_parent = subR;

		if (parent == _root)//parent是根
		{
			_root = subR;//此时的新根就是subR
			_root->_parent = ppNode;
		}
		else//parent不是根
		{
			//将新的根连接到ppNode
			if (ppNode->_left == parent)
			{
				ppNode->_left = subR;
			}
			else
			{
				ppNode->_right = subR;
			}
			subR->_parent = ppNode;
		}
	}

private:
	Node* _root = nullptr;
};

Mymap.h

#pragma once

namespace cp
{
	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;


		iterator begin()
		{
			return _m.Begin();
		}

		iterator end()
		{
			return _m.End();
		}

		const_iterator begin() const
		{
			return _m.Begin();
		}

		const_iterator end() const
		{
			return _m.End();
		}

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

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

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

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

Myset.h

#pragma once

namespace cp
{
	template<class K>
	class set
	{
		struct SetKofT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
	public:
		typedef typename RBTree<K, const K, SetKofT>::Iterator iterator;
		typedef typename RBTree<K, const K, SetKofT>::ConstIterator const_iterator;

		iterator begin()
		{
			return _s.Begin();
		}

		iterator end()
		{
			return _s.End();
		}

		const_iterator begin() const
		{
			return _s.Begin();
		}

		const_iterator end() const
		{
			return _s.End();
		}

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

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

	private:
		RBTree<K, const K, SetKofT> _s;
	};
}

OK,本期分享就到这里,好兄弟我们下期再见~!

结束语:我们的目标是星辰大海!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/792993.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【JavaEE】网络编程——UDP

&#x1f921;&#x1f921;&#x1f921;个人主页&#x1f921;&#x1f921;&#x1f921; &#x1f921;&#x1f921;&#x1f921;JavaEE专栏&#x1f921;&#x1f921;&#x1f921; 文章目录 1.数据报套接字(UDP)1.1特点1.2编码1.2.1DatagramSocket1.2.2DatagramPacket…

汽车预约维修小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;技师管理&#xff0c;技师信息管理&#xff0c;用户预约管理&#xff0c;取消预约管理&#xff0c;订单信息管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;技师信息&a…

接上一回C++:补继承漏洞+多态原理(带图详解)

引子&#xff1a;接上一回我们讲了继承的分类与六大默认函数&#xff0c;其实继承中的菱形继承是有一个大坑的&#xff0c;我们也要进入多态的学习了。 注意&#xff1a;我学会了&#xff0c;但是讲述上可能有一些不足&#xff0c;希望大家多多包涵 继承复习&#xff1a; 1&…

并查集+链表,CF 1131F - Asya And Kittens

一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 1131F - Asya And Kittens 二、解题报告 1、思路分析 本质是拼积木游戏 初始有n块积木&#xff0c;每次两块首尾拼成一块就行&#xff0c;拼接n - 1 次最后会得到一个大积木&#xff0c;我们从左往右输出组…

Nuxt3封装网络请求 useFetch $fetch

前言&#xff1a; 刚接触、搭建Nuxt3项目的过程还是有点懵的&#xff0c;有种摸石头过河的感觉&#xff0c;对于网络请求这块&#xff0c;与之前的Vue3项目有所区别&#xff0c;在Vue项目通常使用axios这个库进行网络请求&#xff0c;但在Nuxt项目并不推荐&#xff0c;因为有内…

【PostgreSQL】Spring boot + Mybatis-plus + PostgreSQL 处理json类型情况

Spring boot Mybatis-plus PostgreSQL 处理json类型情况 一、前言二、技术栈三、背景分析四、方案分析4.1 在PostgreSQL 数据库中直接存储 json 对象4.2 在PostgreSQL 数据库中存储 json 字符串 五、自定义类型处理器5.1 定义类型处理器5.2 使用自定义类型处理器 一、前言 在…

【PowerShell】-1-快速熟悉并使用PowerShell

目录 PowerShell是什么&#xff1f;和CMD的区别&#xff1f; PowerShell的演变 自动化IT管理任务 一些名词 详尽的PowerShell开始之路 1.打开PowerShell&#xff1a; 2.基本命令&#xff1a; &#xff08;1&#xff09;Get-Process &#xff08;2&#xff09;变量赋值…

React Hooks学习笔记

一、usestate的使用方法-初始化state函数 import React, { useState } from "react"; function App() {const [count, setCount] useState(0);return (<div><p>点击{count}次</p><button onClick{() > setCount(count 1)}>点击</bu…

【Linux系统】信号量(初次理解)

五个概念 多个执行流&#xff08;进程&#xff09;&#xff0c;能看到的一份资源&#xff1a;共享资源被保护起来的资源可以叫临界资源&#xff08;同步和互斥&#xff09; --- 用互斥的方式保护共享资源就叫临界资源互斥&#xff1a;任何时刻只能有一个进程在访问共享资源资源…

就业平台小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;学生管理&#xff0c;企业管理&#xff0c;企业类型管理&#xff0c;留言板管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;招聘信息&#xff0c;简历&#xff0c;我的 …

SpringCloud--Eureka集群

Eureka注册中心集群 为什么要集群 如果只有一个注册中心服务器&#xff0c;会存在单点故障&#xff0c;不可以高并发处理所以要集群。 如何集群 准备三个EurekaServer 相互注册&#xff0c;也就是说每个EurekaServer都需要向所有的EureakServer注册&#xff0c;包括自己 &a…

游戏如何应对黑灰产工作室

游戏黑灰产工作室&#xff0c;是指以非法渠道、非法手段通过游戏进行牟利的团伙。使用脚本、外挂是黑灰产工作室的显著特征&#xff0c;其常见的牟利方式有&#xff1a;打金工作室、资源囤积号、初始号、自抽号、代练工作室以及营销欺诈等。 ▲ 常见的游戏黑灰产工作室牟利路径…

PMP证书 怎么报名?

首先&#xff0c;PMP证书相当于某些行业的敲门砖&#xff0c;身处项目管理相关行业的人应该清楚&#xff0c;这个证书&#xff0c;可能是你升职的不可或缺的一把钥匙。首先&#xff0c;我们先来了解一下什么是pmp。 1充分了解PMP PMP&#xff08;项目管理专业人士资格认证&am…

idm站点抓取可以用来做什么 idm站点抓取能抓取本地网页吗 idm站点抓取怎么用 网络下载加速器

在下载工具众多且竞争激烈的市场中&#xff0c;Internet Download Manager&#xff08;简称IDM&#xff09;作为一款专业的下载加速软件&#xff0c;仍然能够赢得众多用户的青睐&#xff0c;这都要得益于它的强大的下载功能。我们在开始使用IDM的时候总是有很多疑问&#xff0c…

「iOS」暑假第一周 —— ZARA的仿写

暑假第一周 ZARA的仿写 文章目录 暑假第一周 ZARA的仿写写在前面viewDidLoad 之中的优先级添加自定义字体下载想要的字体添加至info之中找到字体名字并应用 添加应用图标和启动页面 写在前面 暑假第一周留校学习&#xff0c;对于ZARA进行了仿写&#xff0c;在仿写的过程之中&a…

探索创意无限:独特的平面设计趋势与案例分享

随着平面设计领域的不断发展&#xff0c;平面设计趋势也在不断变化。在一个信息爆炸的时代&#xff0c;设计不仅仅是视觉的表达&#xff0c;更是思想和情感的交流。到了 2024 年&#xff0c;一些新的平面设计趋势已经开始显现&#xff0c;同时一些旧的趋势得到了新的发展和再度…

Jenkins安装部署与配置

目录 前言 Jenkins 的主要功能 Jenkins 的工作流程 一. 环境准备 二. 安装JDK 三. 安装Tomcat 四. 部署Jenkins 五. 浏览器访问 六. 修改超级管理员默认密码 七. 系统配置 八. 安装插件 九. 手动部署插件 前言 Jenkins 是一个开源的自动化服务器&#xff0c;用于…

C# 串口数据转网口实现空气风速风向检测

1.窗体搭建 添加time(定时器) 因为需要风速和风向自动刷新 2.进行网口空气检测 ①服务器连接按钮 // 连接按钮private void button1_Click(object sender, EventArgs e){if (button1.Text "连接"){ConnectSocke();// 连接服务器}else{CloseSocket(); // 关闭服务器…

苹果提出RLAIF:轻量级语言模型编写代码

获取本文论文原文PDF&#xff0c;请在公众号【AI论文解读】留言&#xff1a;论文解读 代码生成一直是一个充满挑战的领域。随着大型语言模型&#xff08;LLM&#xff09;的出现&#xff0c;我们见证了在自然语言理解和生成方面的显著进步。然而&#xff0c;当涉及到代码生成&a…

XD文件打开神器:这个在线工具你一定要试试!

你有没有遇到过对设计师发来的XD文件没有头绪&#xff1f;不知道XD文件深层含义&#xff1f;如何打开XD文件最省时省力&#xff1f;这篇文章告诉你答案。 https://ad.js.design/online/xd/?sourcecsdn&planxy711 XD文件是什么&#xff1f; 事实上&#xff0c;XD文件就是…