【STL】用一棵红黑树封装map和set

news2024/12/24 8:26:52

⭐博客主页:️CS semi主页
⭐欢迎关注:点赞收藏+留言
⭐系列专栏:C++进阶
⭐代码仓库:C++进阶
家人们更新不易,你们的点赞和关注对我而言十分重要,友友们麻烦多多点赞+关注,你们的支持是我创作最大的动力,欢迎友友们私信提问,家人们不要忘记点赞收藏+关注哦!!!

用一棵红黑树封装map和set

  • 一、红黑树原码
  • 二、红黑树模板参数控制
  • 三、红黑树结点当中存储的数据
  • 四、红黑树结点中仿函数
  • 五、正向迭代器
    • 1、框架+构造函数
    • 2、Ref
    • 3、Ptr
    • 4、重载operator==和operator!=
    • 5、operator++ 【重点讲解】
      • (1)情况1:当前结点的右子树有结点
      • (2)情况2:当前结点的右子树为空
      • (3)代码
    • 6、operator--
      • (1)情况1:当前节点的左子树有节点
      • (2)情况2:当前结点的左子树没结点
      • (3)代码
    • 7、正向迭代器在RBTree中的实现
      • (1)begin()
      • (2)end()
      • (3)代码
    • 8、缺陷和改进
  • 六、反向迭代器
    • 1、反向迭代器的实现
    • 2、RBTree中使用反向迭代器
  • 七、代码汇总


一、红黑树原码

传送

二、红黑树模板参数控制

set是Key模型的容器,map是key-value模型的容器,而我们是需要用一种模板来进行实现set和map,所以,我们使用了key-value模型来进行模拟实现set和map,我们将第二个模版参数改成class T,这样能够以示区分原来的红黑树的模版。

在这里插入图片描述

这个T既可能是K值,也可能是key与value共同构成的键值对,我们看一下set容器的实现形式:
在这里插入图片描述

我们再来写一下map的实现形式:

在这里插入图片描述

三、红黑树结点当中存储的数据

那我们红黑树中的结点也是需要进行一下更改的,我们看下图,MySet中第二个模板参数传到RBTree中是传的是Key,而MyMap中第二个模板参数传到RBTree中的是pair<K, V>,这是kv的键值对,而RBTreeNode中接收的是第二个模板参数,我们以下列一下K和T不同的模板形式:

set:K和T都代表着Key。
map:K代表键值Key,T代表键值对pair<K, V>。

所以我们只需要使用第二个模板参数T就可以实现红黑树结点根据不同的容器进行模板实例化,但有同学要问了,为什么我们不能直接在set和map中只使用一个模版参数T呢?而偏偏要使用两个模板参数K和T?
似乎是没什么问题的,对set来讲有没有这个第一个参数K都不影响的,而对于大部分的map情况中也是没有什么影响的,而具有最重大影响的是map的find和erase这两个接口函数,这两个接口函数是需要用到第一个参数K的,所以为了保持所有的接口都能用,只能委屈一下set,让set多一个模板参数K。

在这里插入图片描述

//红黑树结点的定义
template<class T>
struct RBTreeNode
{
	//构造函数
	RBTreeNode(const T& data)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _data(data)
		, _col(RED)
	{}

	//三叉链
	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right;
	RBTreeNode<T>* _parent;

	//存储的数据
	T _data;

	//结点的颜色
	int _col; //红/黑
};

四、红黑树结点中仿函数

一个很简单的现象,参数T:set的上层是K,直接能进行比较,而map上层是pair<K, V>,而我们进行比较map的时候是很难进行pair比较的,因为比较不了,所以我们需要将pair中的K特地摘出来进行比较,那么这个比较就需要通过一个仿函数进行摘出来K进行比较键值对。

仿函数:我们之前写过这种类似的仿函数模型,其实就是要实现一个operator()的功能,这个类就有了类似函数的行为,就是一个仿函数类了。

Map:
在这里插入图片描述

我们Map写了以后,我们思考一下Set写不写?
看似似乎并不用写这个Set,因为我们的Key能直接拿到,但是这只是我们人的视角下是肯定可以的,但是编译器能那么聪明吗?我们了解到,编译器是没有办法进行判断我们传过来的是Map还是Set,所以我们需要委屈一下Set,把Set的仿函数也书写一下:
小故事:今天女朋友说要去逛街买包,你敢不去吗?也就是Set是一个陪逛街的身份,但它必须陪着Map逛街。
在这里插入图片描述

在这里插入图片描述

这样我们根据上面的逻辑图看实例化,是在RBTree中加入一个仿函数,将Set和Map的仿函数比较过程进行传参并实例化,那我们下面用一个Find子函数进行解析一下:

	// 红黑树的查找
	Node* Find(const K& key)
	{
		KeyOfT kot;
		Node* cur = _root;
		while (cur)
		{
			// 当前结点的值大于寻找的结点的值
			if (key < kot(cur->_data))
			{
				cur = cur->_left;
			}
			else if (key > kot(cur->_data))
			{
				cur = cur->_right;
			}
			else
			{
				// 找到了
				return cur;
			}
		}
		return nullptr;
	}

在这里插入图片描述

五、正向迭代器

红黑树的正向迭代器实际上就是对结点指针进行了封装,因此在正向迭代器当中实际上就只有一个成员变量,那就是正向迭代器所封装结点的指针。

// 正向迭代器
template<class T, class Ref, class Ptr>
struct __TreeIterator
{
	typedef RBTreeNode<T> Node; //结点类型
	typedef __TreeIterator<T, Ref, Ptr> Self; //正向迭代器类型

	Node* _node;
};

我们在之前写过有关T,Ref和Ptr,所以我们先介绍一下,T就是我们的模版参数,Ref是解引用操作,Ptr是指针操作。

1、框架+构造函数

// 正向迭代器
template<class T, class Ref, class Ptr>
struct __TreeIterator
{
	typedef RBTreeNode<T> Node; //结点类型
	typedef __TreeIterator<T, Ref, Ptr> Self; //正向迭代器类型
	// 构造函数
	__TreeIterator(Node* node)
		:_node(node)
	{}
	Node* _node;
}

解释一下:T是第一个模版参数,定义类型的,Ref是解引用操作的模版参数,Ptr是指针指向的模版参数。

2、Ref

要对于一个结点解引用的操作,我们直接返回这个结点的值即可。

	// 正向迭代器解引用操作
	Ref operator*()
	{
		return _node->_data;
	}

3、Ptr

要对于一个结点进行->的操作的时候,我们直接返回这个结点数据的指针即可。

	// 正向迭代器指向操作
	Ptr operator->()
	{
		return &_node->_data;
	}

4、重载operator==和operator!=

直接使用Self类型进行判断这两个迭代器是否相同。

	// 判断两个正向迭代器是否不同
	bool operator!=(const Self& s) const
	{
		return _node != s._node;
	}

	// 判断两个正向迭代器是否相同
	bool operator==(const Self& s) const
	{
		return _node == s._node;
	}

5、operator++ 【重点讲解】

我们要实现这个operator++,我们首先需要了解这个红黑树的遍历打印是中序遍历打印,也就是先左子树,再根,再右子树,所以我们如下两种情况:

(1)情况1:当前结点的右子树有结点

找该节点的右子树的最左节点。

在这里插入图片描述

(2)情况2:当前结点的右子树为空

++操作后在该结点的祖先结点中,找到孩子不在父亲右的祖先。

在这里插入图片描述

(3)代码

	// 正向迭代器++操作
	Self operator++()
	{
		// 结点的右子树不为空,找右子树的最左结点
		if (_node->_right != nullptr)
		{
			Node* R_left = _node->_right;
			while (R_left->_left != nullptr)
			{
				R_left = R_left->_left;
			}
			_node = R_left;
		}
		// 结点的右子树为空,找祖先结点不为右的结点
		else
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent&& cur == parent->_right)
			{
				cur = parent;
				parent = parent->_parent;
			}
			_node = parent;
		}
		return *this;
	}

6、operator–

即遍历当前结点的前一个结点,与++正好相反。

1、如果当前结点的左子树不为空,则–操作后应该找到其左子树当中的最右结点。
2、如果当前结点的左子树为空,则–操作后应该在该结点的祖先结点中,找到孩子不在父亲左的祖先。

(1)情况1:当前节点的左子树有节点

–是找左子树的最右结点。
在这里插入图片描述

(2)情况2:当前结点的左子树没结点

在这里插入图片描述

(3)代码

	// 正向迭代器--操作
	Self operator--()
	{
		// 结点的左子树不为空,找左子树中的最右节点
		if (_node->_left != nullptr)
		{
			Node* L_right = _node->_left;
			while (L_right->_right != nullptr)
			{
				L_right = L_right->_right;
			}
			_node = L_right;
		}
		// 节点的左子树为空,找祖先结点中,找到孩子不在父亲左的祖先
		else
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_left)
			{
				cur = parent;
				parent = parent->_parent;
			}
			_node = parent;
		}
		return *this;
	}

7、正向迭代器在RBTree中的实现

正向迭代器分为begin()和end(),所以我们分析一下begin()和end():

我们要将iterator放到public当中,让外部可以拿到。

(1)begin()

begin()就是我们前面所提的红黑树的第一个位置,而第一个位置则是整颗红黑树,begin函数返回中序序列当中第一个结点的正向迭代器,即最左结点。

	typedef __TreeIterator<T, T&, T*> iterator;
	// 最左节点
	iterator begin()
	{
		Node* left = _root;
		while (left && left->_left != nullptr)
		{
			left = left->_left;
		}
		// 返回最左结点的迭代器
		return iterator(left);
	}

(2)end()

end()就是我们前面所提到的红黑树的最末尾的位置的下一个位置,那么就是nullptr!

	typedef __TreeIterator<T, T*, T&> iterator;
	// end()是整个树的最末尾结点的后一个位置
	iterator end()
	{
		return iterator(nullptr);
	}

(3)代码

template<class K, class T, class KeyOfT>
class RBTree
{
	typedef RBTreeNode<T> Node;

public:
	typedef __TreeIterator<T, T*, T&> iterator;

	// 最左节点
	iterator begin()
	{
		Node* left = _root;
		while (left && left->_left != nullptr)
		{
			left = left->_left;
		}
		// 返回最左结点的迭代器
		return iterator(left);
	}

	// end()是整个树的最末尾结点的后一个位置
	iterator end()
	{
		return iterator(nullptr);
	}
private:
	Node* _root;
};

8、缺陷和改进

我们大家不知道发现了没有,上述所写的end()是指向的最后一个结点的下一个位置,即nullptr,但我们的C++SGI版本中不是这样写的,其end()是指向最后一个元素的,所以我们来看一下SGI版本下的end()封装是怎么封装的:

在这里插入图片描述

我们看,在这个版本中,多了个header结点刚刚好是13根节点的父节点,这个header结点的左边指向这棵红黑树的最左节点象征着begin(),这个header结点的右边指向这棵红黑树的最右结点象征着rbegin()。实现end()和rend()时,直接用头结点构造出正向和反向迭代器即可。此后,通过对逻辑的控制,就可以实现end()进行–操作后得到最后一个结点的正向迭代器。

六、反向迭代器

1、反向迭代器的实现

反向迭代器我们根本都不需要一步一步写下来了,我们只需要用正向迭代器进行封装反向迭代器即可,如下代码:

在这里插入图片描述

//反向迭代器 -- 根据正向迭代器封装
template<class Iterator>
struct ReverseIterator
{
	typedef ReverseIterator<Iterator> Self; //反向迭代器的类型

	// 这里为了能够让反向迭代器能够拿到正向迭代器的解引用和指针
	typedef typename Iterator::reference Ref; //结点指针的解引用*
	typedef typename Iterator::pointer Ptr; //结点指针->

	//构造函数
	ReverseIterator(Iterator rit)
		:_rit(rit)
	{}

	// 和正向迭代器一样
	Ref operator*()
	{
		return *_rit;
	}

	// 和正向迭代器一样
	Ptr operator->()
	{
		return _rit.operator->(); 
	}

	  // ++操作就是正向迭代器的--操作
	Self& operator++()
	{
		--_rit;
		return *this;
	}

	// --操作就是正向迭代器的++操作
	Self& operator--()
	{
		++_rit;
		return *this;
	}

	// 不等号一样
	bool operator!=(const Self& s) const
	{
		return _rit != s._rit;
	}

	// 等号也一样
	bool operator==(const Self& s) const
	{
		return _rit == s._rit;
	}
	
	Iterator _rit;
};

2、RBTree中使用反向迭代器

template<class K, class T, class KeyOfT>
class RBTree
{
	typedef RBTreeNode<T> Node;

public:
	typedef __TreeIterator<T, T*, T&> iterator;
	typedef ReverseIterator<iterator> reverse_iterator;

	// 最左节点
	iterator begin()
	{
		Node* left = _root;
		while (left && left->_left != nullptr)
		{
			left = left->_left;
		}
		// 返回最左结点的迭代器
		return iterator(left);
	}

	// end()是整个树的最末尾结点的后一个位置
	iterator end()
	{
		return iterator(nullptr);
	}

	// 最右结点
	reverse_iterator rbegin()
	{
		Node* right = _root;
		while (right && right->_left != nullptr)
		{
			right = right->_right;
		}
		// 返回最右结点的迭代器
		return reverse_iterator(iterator(right));
	}

	// end()是整个树的最末尾结点的后一个位置
	reverse_iterator rend()
	{
		return reverse_iterator(iterator(nullptr));
	}
private:
	Node* _root;
};

七、代码汇总

Set.h:

#include"MyRBTree.h"

namespace JRH
{
	template<class K, class T>
	class MySet
	{
		// 仿函数
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};

	public:
		typedef typename RBTree<K, K, SetKeyOfT>::iterator iterator; //正向迭代器
		typedef typename RBTree<K, K, SetKeyOfT>::reverse_iterator reverse_iterator; //反向迭代器

		// begin()
		iterator begin()
		{
			return _t.begin();
		}

		// end()
		iterator end()
		{
			return _t.end();
		}

		// rbegin()
		reverse_iterator rbegin()
		{
			return _t.rbegin();
		}

		// rend()
		reverse_iterator rend()
		{
			return _t.rend();
		}

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

		// 删除
		void erase(const K& key)
		{
			return _t.Erase(key);
		}

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

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



map.h

#include"MyRBTree.h"

namespace JRH
{
	template<class K, class V>
	class MyMap

	{	//仿函数
		struct MapKeyOfT
		{
			const K& operator()(const pair<const K, V>& kv) //返回键值对当中的键值Key
			{
				return kv.first;
			}
		};
	public:
		typedef typename RBTree<K, pair<K, V>, MapKeyOfT>::iterator iterator; //正向迭代器
		typedef typename RBTree<K, pair<K, V>, MapKeyOfT>::reverse_iterator reverse_iterator; //反向迭代器

		// begin()
		iterator begin()
		{
			return _t.begin();
		}

		// end()
		iterator end()
		{
			return _t.end();
		}

		// rbegin()
		reverse_iterator rbegin()
		{
			return _t.rbegin();
		}

		// rend()
		reverse_iterator rend()
		{
			return _t.rend();
		}

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

		// 删除
		void erase(const K& key)
		{
			return _t.Erase(key);
		}

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

		//[]运算符重载函数
		V& operator[](const K& key)
		{
			pair<iterator, bool> ret = insert(make_pair(key, V()));
			iterator it = ret.first;
			return it->second;
		}

	private:
		RBTree<K, pair<K, V>, MapKeyOfT> _t;
	};
}



正向和反向迭代器:

// 正向迭代器
template<class T, class Ref, class Ptr>
struct __TreeIterator
{
	typedef RBTreeNode<T> Node; //结点类型
	typedef __TreeIterator<T, Ref, Ptr> Self; //正向迭代器类型

	// 为了让下面反向迭代器拿到
	typedef typename Ref reference;
	typedef typename Ptr pointer;
	// 构造函数
	__TreeIterator(Node* node)
		:_node(node)
	{}

	// 正向迭代器解引用操作
	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;
	}

	// 正向迭代器++操作
	Self operator++()
	{
		// 结点的右子树不为空,找右子树的最左结点
		if (_node->_right != nullptr)
		{
			Node* R_left = _node->_right;
			while (R_left->_left != nullptr)
			{
				R_left = R_left->_left;
			}
			_node = R_left;
		}
		// 结点的右子树为空,找祖先结点不为右的结点
		else
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent&& cur == parent->_right)
			{
				cur = parent;
				parent = parent->_parent;
			}
			_node = parent;
		}
		return *this;
	}

	// 正向迭代器--操作
	Self operator--()
	{
		// 结点的左子树不为空,找左子树中的最右节点
		if (_node->_left != nullptr)
		{
			Node* L_right = _node->_left;
			while (L_right->_right != nullptr)
			{
				L_right = L_right->_right;
			}
			_node = L_right;
		}
		// 节点的左子树为空,找祖先结点中,找到孩子不在父亲左的祖先
		else
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_left)
			{
				cur = parent;
				parent = parent->_parent;
			}
			_node = parent;
		}
		return *this;
	}

	Node* _node;
};


//反向迭代器 -- 根据正向迭代器封装
template<class Iterator>
struct ReverseIterator
{
	typedef ReverseIterator<Iterator> Self; //反向迭代器的类型

	// 这里为了能够让反向迭代器能够拿到正向迭代器的解引用和指针
	typedef typename Iterator::reference Ref; //结点指针的解引用*
	typedef typename Iterator::pointer Ptr; //结点指针->

	//构造函数
	ReverseIterator(Iterator rit)
		:_rit(rit)
	{}

	// 和正向迭代器一样
	Ref operator*()
	{
		return *_rit;
	}

	// 和正向迭代器一样
	Ptr operator->()
	{
		return _rit.operator->(); 
	}

	  // ++操作就是正向迭代器的--操作
	Self& operator++()
	{
		--_rit;
		return *this;
	}

	// --操作就是正向迭代器的++操作
	Self& operator--()
	{
		++_rit;
		return *this;
	}

	// 不等号一样
	bool operator!=(const Self& s) const
	{
		return _rit != s._rit;
	}

	// 等号也一样
	bool operator==(const Self& s) const
	{
		return _rit == s._rit;
	}
	
	Iterator _rit;
};

RBtree改装:

#include<algorithm>

#include<iostream>
using namespace std;

enum Col
{
	BLACK,
	RED
};

//红黑树结点的定义
template<class T>
struct RBTreeNode
{
	//构造函数
	RBTreeNode(const T& data)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _data(data)
		, _col(RED)
	{}

	//三叉链
	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right;
	RBTreeNode<T>* _parent;

	//存储的数据
	T _data;

	//结点的颜色
	int _col; //红/黑
};



template<class K, class T, class KeyOfT>
class RBTree
{
	typedef RBTreeNode<T> Node;

public:
	typedef __TreeIterator<T, T&,T*> iterator;
	typedef ReverseIterator<iterator> reverse_iterator;

	// 构造函数
	RBTree()
		:_root(nullptr)
	{}

	//赋值运算符重载
	RBTree<K, T, KeyOfT>& operator=(RBTree<K, T, KeyOfT> t)
	{
		swap(_root, t._root);
		return *this;
	}

	//析构函数
	~RBTree()
	{
		_Destroy(_root);
		_root = nullptr;
	}

	// 最左节点
	iterator begin()
	{
		Node* left = _root;
		while (left && left->_left != nullptr)
		{
			left = left->_left;
		}
		// 返回最左结点的迭代器
		return iterator(left);
	}

	// end()是整个树的最末尾结点的后一个位置
	iterator end()
	{
		return iterator(nullptr);
	}

	// 最右结点
	reverse_iterator rbegin()
	{
		Node* right = _root;
		while (right && right->_left != nullptr)
		{
			right = right->_right;
		}
		// 返回最右结点的迭代器
		return reverse_iterator(iterator(right));
	}

	// end()是整个树的最末尾结点的后一个位置
	reverse_iterator rend()
	{
		return reverse_iterator(iterator(nullptr));
	}

	// 红黑树的查找
	Node* Find(const K& key)
	{
		KeyOfT kot;
		Node* cur = _root;
		while (cur)
		{
			// 当前结点的值大于寻找的结点的值
			if (key < kot(cur->_data))
			{
				cur = cur->_left;
			}
			else if (key > kot(cur->_data))
			{
				cur = cur->_right;
			}
			else
			{
				// 找到了
				return cur;
			}
		}
		return nullptr;
	}

	// 插入
	pair<iterator, bool> Insert(const T& data)
	{
		KeyOfT kot;

		// 一棵空树
		if (_root == nullptr)
		{
			// 创建新结点 + 颜色初始化为黑色
			_root = new Node(data);
			_root->_col = BLACK; // 根节点得是黑的
			return make_pair(iterator(_root), true);
		}

		// 先找到 -- 利用二叉搜索树的方法进行查找
		Node* cur = _root;
		Node* parent = nullptr;
		// 左小右大
		while (cur)
		{
			// 当前结点值大于待插入结点值,往左子树走
			if (kot(cur->_data) > kot(data))
			{
				parent = cur;
				cur = cur->_left;
			}
			// 当前结点值小于待插入结点值,往右子树走
			else if (kot(cur->_data) < kot(data))
			{
				parent = cur;
				cur = cur->_right;
			}
			// 待插入结点的值和当前结点的值相等,插入失败
			else
			{
				return make_pair(iterator(cur), false);
			}
		}

		// 将当前结点的值插入进去
		cur = new Node(data); // new一个新的结点
		cur->_col = RED;
		Node* newnode = cur; // 记录新插入的结点
		// 新插入的节点值小于父节点的节点值,插入到parent的左边
		if (kot(data) < kot(parent->_data))
		{
			parent->_left = cur;
			cur->_parent = parent;
		}
		// 新插入的节点值小于父节点的节点值,插入到parent的左边
		else
		{
			parent->_right = cur;
			cur->_parent = parent;
		}

		// 新插入结点的父节点是红色的,需要做出调整
		while (parent && parent->_col == RED)
		{
			Node* grandfather = parent->_parent; // parent是红色,则其父结点一定存在
			// 以grandparent左右孩子为分界线,分成if和else
			if (parent == grandfather->_left) // 左孩子
			{
				Node* uncle = grandfather->_right;
				if (uncle && uncle->_col == RED)// 情况一:uncle存在且为红色
				{
					// 颜色调整
					grandfather->_col = RED;
					uncle->_col = parent->_col = BLACK;

					// 继续往上处理
					cur = grandfather;
					parent = cur->_parent;
				}
				else// 情况二:uncle存在且为黑色 / 情况三:uncle不存在
				{
					// 用左右孩子分为两半,一半是if用来表示在左孩子,一半是else用来表示在右孩子
					if (cur == parent->_left)
					{
						//   g
						//  p
						// c
						// 右单旋
						RoateR(grandfather);

						// 颜色调整
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
						//    g
						//  p
						//      c
						// 左右双旋
						RoateLR(grandfather);

						// 颜色调整
						cur->_col = BLACK;
						grandfather->_col = RED;
					}
					break;
				}
			}
			else // 右孩子
			{
				Node* uncle = grandfather->_left;
				if (uncle && uncle->_col == RED)// 情况一:uncle存在且为红色
				{
					// 颜色调整
					grandfather->_col = RED;
					uncle->_col = parent->_col = BLACK;

					// 继续往上处理
					cur = grandfather;
					parent = cur->_parent;
				}
				else// 情况二:uncle存在且为黑色 / 情况三:uncle不存在
				{
					// 用左右孩子分为两半,一半是if用来表示在左孩子,一半是else用来表示在右孩子
					if (cur == parent->_right)
					{
						//   g
						//     p
						//      c
						// 左单旋
						RoateL(grandfather);

						// 颜色调整
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
						//    g
						//      p
						//    c
						// 右左双旋
						RoateRL(grandfather);

						// 颜色调整
						cur->_col = BLACK;
						grandfather->_col = RED;
					}
					break;
				}
			}
		}
		_root->_col = BLACK;
		return make_pair(iterator(newnode), true);
	}

	// 左单旋
	void RoateL(Node* parent)
	{
		// 三叉链
		Node* subr = parent->_right;
		Node* subrl = subr->_left;
		Node* ppnode = parent->_parent;

		// subrl与parent的关系
		parent->_right = subrl;
		if (subrl)
			subrl->_parent = parent;

		// subl和parent的关系
		subr->_left = parent;
		parent->_parent = subr;

		// ppnode和subr的关系
		if (ppnode == nullptr)
		{
			_root = subr;
			subr->_parent = nullptr;
		}
		else
		{
			if (ppnode->_left == parent)
			{
				ppnode->_left = subr;
			}
			else
			{
				ppnode->_right = subr;
			}
			subr->_parent = ppnode;
		}
	}

	// 右单旋
	void RoateR(Node* parent)
	{
		// 三叉链
		Node* subl = parent->_left;
		Node* sublr = subl->_right;
		Node* ppnode = parent->_parent;


		//sublr和parent之间的关系
		parent->_left = sublr;
		if (sublr)
			sublr->_parent = parent;

		//subl和parent的关系
		subl->_right = parent;
		parent->_parent = subl;


		//ppnode 和 subl的关系
		if (ppnode == nullptr)
		{
			_root = subl;
			subl->_parent = nullptr;
		}
		else
		{
			if (ppnode->_left == parent)
			{
				ppnode->_left = subl;
			}
			else
			{
				ppnode->_right = subl;
			}
			subl->_parent = ppnode;
		}
	}

	// 左右双旋
	void RoateLR(Node* parent)
	{
		RoateL(parent->_left);
		RoateR(parent);
	}

	// 右左双旋
	void RoateRL(Node* parent)
	{
		RoateR(parent->_right);
		RoateL(parent);
	}


private:
	//析构函数子函数
	void _Destroy(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}
		_Destroy(root->_left);
		_Destroy(root->_right);
		delete root;
	}

	Node* _root;
};

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

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

相关文章

独立按键控制LED亮灭、独立按键控制LED状态、独立按键控制LED显示二进制、独立按键控制LED移位——“51单片机”

各位CSDN的uu们你们好呀&#xff0c;今天依旧是小雅兰的51单片机的内容&#xff0c;内容主要是&#xff1a;独立按键控制LED亮灭、独立按键控制LED状态、独立按键控制LED显示二进制、独立按键控制LED移位&#xff0c;下面&#xff0c;让我们进入51单片机的世界吧&#xff01;&a…

【探索排序算法的魅力:优化、性能与实用技巧】

本章重点 排序的概念及其运用 常见排序算法的实现 排序算法复杂度及稳定性分析 1.排序的概念及其运用 1.1排序的概念 排序&#xff1a;所谓排序&#xff0c;就是使一串记录&#xff0c;按照其中的某个或某些关键字的大小&#xff0c;递增或递减的排列起来的操作。 稳定性…

Edge扩展插件推荐专业视频下载器

专业视频下载器&#xff0c;这款扩展插件非常好用&#xff0c;强烈推荐。只要能打开的视频&#xff0c;都能下载。 安装完成是这样的&#xff1a; 有用记得点赞。

编程前置:怎么知道一句话的重点?

怎么知道一句话的重点&#xff1f; <small> 之所以要这个问题&#xff0c;是因为① 对标题进行分词 ② 找到标题中的重点词 ③ 然后找到主题中唯一的词语 ④ 然后对这个词语进行绘图说和看&#x1f440;来看&#x1f440;去&#xff0c;也就是文字成图的步骤啦&#xff…

Linux作业2

Linux中的 stdin 、stderr、stdout分别是什么意思 在 Linux 中&#xff0c;stdin、stdout 和 stderr 是标准的输入、标准的输出和标准的错误的缩写&#xff0c;它们是与终端相关联的默认文件描述符&#xff0c;用于处理输入和输出。以下是它们的详细含义&#xff1a; stdin&am…

日期范围搜索

1.日期范围选择界面 <?xml version"1.0" encoding"utf-8"?> <ScrollViewandroid:layout_width"fill_parent"android:layout_height"fill_parent"xmlns:android"http://schemas.android.com/apk/res/android">…

构建一个TypeScript环境的node项目

本文 我们用一种不太一样的方式来创建项目 这里 我们事先创建了一个文件夹作为项目目录 然后打开项目终端 输入 npm init然后 在新弹出的对话框中 大体就是 名字随便写一个 然后 后面的回车&#xff0c;到最后一个输入 yes 然后回车 这样 我们就有一个基础的 node项目结构了…

Alibaba Cloud Linux 3安装Docker

出现以上报错&#xff0c;进行以下操作&#xff1a; cd /etc/yum.repos.d/ rm -rf docker-ce.repo 然后进行docker安装&#xff08;以社区版为例&#xff09; 1.添加docker-ce的dnf源 dnf config-manager --add-repohttps://mirrors.aliyun.com/docker-ce/linux/centos/do…

CCC数字钥匙设计【NFC】 --车主配对流程介绍

1、车主配对流程介绍 车主配对流程可以通过车内NFC进行&#xff0c;若支持UWB测距&#xff0c;也可以通过蓝牙/UWB进行&#xff0c;本文主要介绍通过NFC进行车主配对的流程。 整个配对流程相对较为复杂&#xff0c;本文主要梳理整体的步骤流程&#xff0c;其中的每个细节流程未…

Linux——补充点(进程切换及页表映射)

目录 补充点1&#xff1a;进程地址空间堆区管理 补充点2&#xff1a;Linux内核进程上下文切换 补充点3&#xff1a;页表映射 补充点4&#xff1a;两级页表 补充点1&#xff1a;进程地址空间堆区管理 Linux内核通过一个被称为进程描述符的task_struct结构体来管理进程&#…

fashion_mnist.load_data()出现[winError 10054] 远程主机强迫关闭了一个现有的连接解决方法

我已经解决完了&#xff0c;之前错误大概是下载超时失败&#xff0c;国外资源嘛&#xff0c;懂得读懂&#xff0c;一般这种情况&#xff0c;两种解决思路&#xff1a;第一种搭个梯子&#xff0c;这种治根&#xff1b;第二种就是像我一样找一个免费资源下载过来即可&#xff0c;…

面试题:线程池灵魂8连问,你挡的住吗?

文章目录 1. 面试官&#xff1a;日常工作中有用到线程池吗&#xff1f;什么是线程池&#xff1f;为什么要使用线程池&#xff1f;2. 面试官&#xff1a;ThreadPoolExecutor 都有哪些核心参数&#xff1f;3. 面试官&#xff1a;什么是阻塞队列&#xff1f;说说常用的阻塞队列有哪…

SVN相关-比较差异的时候哪边是最新的

SVN相关-比较差异的时候哪边是最新的 SVN相关-比较差异的时候哪边是最新的 SVN相关-比较差异的时候哪边是最新的

毛玻璃时钟特效

效果展示 页面结构组成 从上述的效果展示可以看到&#xff0c;我们的背景图片是有三个色块组成&#xff0c;为了能够掌握linear-gradient属性&#xff0c;所以我们背景的三个色块可以采用此属性来实现。 而时钟的数字我们采用背景图片实现&#xff0c;而三个指针我们是用元素…

【夏虫语冰】测试服务器端口是否打开(命令行、Python)

文章目录 1、简介2、命令行2.1 telnet2.1.1 工具简介2.1.2 工具配置2.1.3 工具使用 2.2 curl2.2.1 工具简介2.2.1 工具下载2.2.1 工具使用 2.3 wget2.3.1 工具简介2.3.2 工具下载2.3.2 工具使用 2.4 nc2.4.1 工具简介2.4.2 工具安装2.4.3 工具使用 2.5 ssh2.5.1 工具简介2.5.2 …

【OpenMV】AprilTag标记跟踪 NCC模板匹配 测距与测量物体大小

目录 AprilTag标记跟踪 NCC模板匹配 测距以及测量物体大小 识别乒乓球的距离与大小 红色矩形与蓝色矩形同时识别 AprilTag标记跟踪 Tag36h11&#xff0c;Tag25h9&#xff0c;Tag16h5 Tag36h11信息量更大&#xff0c;更准确 # AprilTags Example # # This example show…

STM32晶振的选择与计算

目录 1、石英晶体特性和型号2、振荡器理论2.1负电阻2.2跨导2.3负阻振荡器原理 3、皮尔斯振荡器设计3.1 皮尔斯振荡器简介3.2反馈电阻器3.3负载电容3.4振荡器跨导3.5驱动电平和外部电阻计算3.5.1计算驱动电平3.5.2另一种驱动电平测量方法3.5.3计算外部电阻 3.6启动时间3.7晶体拉…

Python_面向对象

面向对象编程&#xff08;Object-Oriented Programming&#xff0c;OOP&#xff09;是一种编程范式&#xff0c;它将数据和操作数据的方法组合在一起&#xff0c;以便将数据和行为视为一个整体。这种编程范式的历程可以追溯到20世纪60年代&#xff0c;但直到80年代才开始流行。…

【C语言】【结构体的位段】位段的内存分配及注意事项

1.什么是位段&#xff1a; struct A { int _a:2; int _b:5; int _c:10; int _d:30; };1.A就是一个位段类型的变量&#xff0c;位表示比特位&#xff0c;意思是 A中的变量申请内存的比特位数 比如 _a要占 2 个bit 2.位段的成员必须是 int ,unsigned int ,signed int 类型的&…

瑞吉shardingjdbc4.0.0-RC1-->RC2 读写分离示例错误排查

linux环境&#xff1a;CentOS7.8mysql5.7.29 idea&#xff1a;jdk1.8maven3.5 框架&#xff1a;springboot2.4.5 mybatisplus3.4.2(mybatis-plus-boot-starter)sharding-jdbc4.0.0-RC2(sharding-jdbc-spring-boot-starter兼容性问题由1改成2)druid B站项目视频&#xff1a;…