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

news2024/9/9 7:48:18

前言

前面我们已经对红黑树做了介绍和实现,本期我们来对红黑树进一步改造,然后基于改造后的红黑树封装出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,本期分享就到这里,好兄弟我们下期再见~!

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

小程序项目记录

写小程序遇到的问题&#xff1a; 1、如何发行小程序 第一步点击“发行” 然后选择“小程序-微信(仅适用于uni-app)” 然后会弹出一个这样的框 微信小程序名称和AppId会自动带入 然后控制台会出现这些信息 注意&#xff1a;生成的这个路径build为线上环境部署所使用路径&…

Hydra-MDP: 端到端多模态规划与多目标 Hydra 蒸馏

Hydra-MDP: End-to-end Multimodal Planning with Multi-target Hydra-Distillation Hydra-MDP: 端到端多模态规划与多目标 Hydra 蒸馏 Abstract We propose Hydra-MDP, a novel paradigm employing multiple teachers in a teacher-student model. This approach uses know…

音质强者悠律Ringbuds pro,时尚与音质并存

首次将手机使用的“素皮”材料用在充电仓的设计上&#xff0c;不仅手感好&#xff0c;质感直接拉满&#xff0c;而这样的工业设计不仅获得了很多消费者的喜爱&#xff0c;同时也荣获了世界知名的红点设计大奖&#xff0c;此奖项并一直被冠以“国际工业设计的奥斯卡”之称。可见…

Android 使用 Debug.startMethodTracing 分析方法耗时

参考 Generate Trace Logs by Instrumenting Your App 官网提供了 trace 工具来分析方法耗时。 生成 trace 文件 package com.test.luodemo.trace;import androidx.appcompat.app.AppCompatActivity;import android.os.Bundle; import android.os.Debug; import android.uti…

从点击到转化,Xinstall解析移动广告全链路数据,优化ROI的秘密

在疫情的持续影响下&#xff0c;全球营销环境正经历着前所未有的变革。品牌广告主们面对挑战&#xff0c;展现出前所未有的积极与韧性。移动端广告投放的持续增长&#xff0c;尤其是短视频和图文广告的兴起&#xff0c;为品牌带来了新的机遇。然而&#xff0c;如何在激烈的市场…

Dify 与 Xinference 最佳组合 GPU 环境部署全流程

背景介绍 在前一篇文章 RAG 项目对比 之后&#xff0c;确定 Dify 目前最合适的 RAG 框架。本次就尝试在本地 GPU 设备上部署 Dify 服务。 Dify 是将模型的加载独立出去的&#xff0c;因此需要选择合适的模型加载框架。调研一番之后选择了 Xinference&#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 使用自定义类型处理器 一、前言 在…

微信闪退怎么回事?实用技巧助你轻松应对

在使用微信的过程中&#xff0c;偶尔会遇到闪退的问题&#xff0c;这不仅影响我们的日常沟通&#xff0c;还可能导致重要信息的丢失。那么&#xff0c;微信闪退怎么回事呢&#xff1f;闪退的原因可能有很多&#xff0c;包括软件问题、手机存储不足、系统不兼容等。本文将详细分…

好用的IP反查接口(2)

IP-地理信息查询接口-本地化 参考&#xff1a; 通过Ip查询对应地址,Ip2location全球IP地址网段-CSDN博客 因为在线接口有限制&#xff08;毕竟别人提供服务&#xff0c;服务器&#xff0c;数据维护&#xff0c;域名啥的都要费用&#xff09;&#xff0c; 所以本地化服务的需…

vue2学习笔记-官网使用指南和搭建开发环境

官网使用指南 官网地址&#xff1a;介绍 — Vue.js 1、学习 1.1 教程和API 最重要的两个板块。API是VUE的字典&#xff0c;需要时来查阅。 1.2、风格指南 如何写出风格优雅的VUE代码。规则分为四类&#xff1a;必要的&#xff0c;强烈推荐、推荐、谨慎使用。 1.3、示例 …

C嘎嘎类与对象上篇

类的定义 1. class为定义类的关键字&#xff0c;Stack为类的名字&#xff0c;{}中为类的主体&#xff0c;注意类定义结束时后⾯分号不能省略 。类体中内容称为类的成员&#xff1a;类中的变量称为类的属性或成员变量; 类中的函数称为类的⽅法或者成员函数。 2. C中struct也可以…

C++入门基础题:数组元素逆序(C++版互换方式)

1.题目&#xff1a; 数组元素逆置案例描述: 请声明一个5个元素的数组&#xff0c;并且将元素逆置. (如原数组元素为:1,3,2,5,4;逆置后输出结果为:4,5,2,3,1) 2.图解思路&#xff1a; 3.代码演示&#xff1a; #include<iostream>using namespace std;int main(){int a…

为什么使用代理IP无法访问网站

代理IP可以为用户在访问网站时提供更多的便利性和匿名性&#xff0c;但有时用户使用代理IP后可能会遇到无法访问目标网站的问题。这可能会导致用户无法完成所需的业务要求&#xff0c;给用户带来麻烦。使用代理IP时&#xff0c;您可能会因为各种原因而无法访问您的网站。以下是…

网络安全应急响应信息收集利器-Eagle_Eye

项目介绍: 网络安全应急响应信息收集利器 - Eagle_Eye&#xff1a;您的终端信息自动收集专家 在网络安全的紧急时刻&#xff0c;每一秒都至关重要。Eagle_Eye&#xff0c;这款专为应急响应设计的工具&#xff0c;如同一位随时待命的侦察兵&#xff0c;能够在危机时刻迅速收集…

插片式远程 I/O模块:Profinet总线耦合器在SIMATIC Manager配置

XD9000是Profinet总线耦合器&#xff0c;单个耦合器最多可扩展32个I/O模块&#xff01;本文将详细介绍如何在SIMATIC Manager中配置插片式远程 I/O模块的Profinet总线耦合器&#xff0c;帮助您更好地应用这一技术。 一、SIMATIC Manager软件组态步骤&#xff1a; 1、创建工程&…

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

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

Qt开发 | C++ Qt开发环境安装(Qt5.15.2、VS2019、vsaddin2.8) | QtCreator和VS2019的基本使用和设置 | 添加第三方模块

文章目录 一、Qt简介二、C Qt开发环境安装1.Visual Studio 2019安装2.Qt5.15.2安装3.vsaddin2.8安装 三、QtCreator和VS2019的基本使用1.QtCreator的基本使用1.1 创建项目1.2 Qtcreator基本设置 2.VS2019的基本使用2.1 创建项目2.2 Visual Studio 2019的基本设置 3.如何添加第三…

2款强大到离谱电脑软件,都是效率神器

板栗看板 板栗看板是一款在线可视化项目协作工具&#xff0c;旨在通过自动化、数据化和智能化的手段提升团队协作效率。它基于精益生产的看板理论&#xff0c;具有扁平、透明和灵活的特性&#xff0c;能够帮助个人和团队提高生产效率。 该工具支持多人任务协作&#xff0c;用户…

uni-app/vue项目如何封装全局消息提示组件

效果图&#xff1a; 第一步&#xff1a;封装组件和方法&#xff0c;采用插件式注册&#xff01; 在项目目录下新建components文件夹&#xff0c;里面放两个文件&#xff0c;分别是index.vue和index.js. index.vue&#xff1a; <template><div class"toast&quo…

前端埋点数据收集和数据上报

原文地址 什么是埋点 学名叫时间追踪(Event Tracking), 主要针对用户行为或者业务过程进行捕获&#xff0c;处理和发送相关技术及实施过程. 埋点是数据领域的一个专业术语&#xff0c;也是互联网领域的俗称&#xff0c;是互联网领域的俗称 埋点是产品数据分析的基础&#xf…