【C++】用红黑树封装map、set

news2025/1/21 0:57:14

用红黑树封装map、set

  • 1. 红黑树
    • 1.1 模板参数的控制
      • 1.1.1 Value
      • 1.1.2 KeyOfValue
    • 1.2 正向迭代器
      • 1.2.1 构造函数
      • 1.2.2 begin()+end()
      • 1.2.3 operator++()
      • 1.2.4 operator--()
      • 1.2.5 operator*()
      • 1.2.6 operator->()
      • 1.2.7 operator==()
      • 1.2.8 operator!=()
      • 1.2.9 总代码
    • 1.3 反向迭代器
      • 1.3.1 rbegin()+rend()
      • 1.3.2 总代码
    • 1.4 find()
    • 1.5 insert()
  • 2. Map
    • 2.1 operator[]
  • 3. typename作用
  • 4. 完整代码
    • 4.1 Map.h
    • 4.2 Set.h
    • 4.3 RBTree.h

1. 红黑树

  • set是K模型,map是KV模型,二者底层都是使用红黑树来实现的,所以我们可以将红黑树设置为模板,即:set、map复用同一个类模板的红黑树。

1.1 模板参数的控制

1.1.1 Value

  • Value决定你是k模型的set、还是KV模型的map。

map、set的模板参数value.png

enum Color {  //枚举,一一列举出事物具有的所有可能
	Red,  //枚举常量,给枚举变量进行赋值
	Black,
};

template<class T>//红黑树的节点
struct RBTreeNode {
	typedef RBTreeNode<T> Node;

	//三叉链-》优点:便于查找孩子、父亲节点
	Node* _left;      //该节点的左孩子
	Node* _right;    //该节点的右孩子
	Node* _parent;  //该节点的父亲,便于向上更新
	T _data;
	Color _col;

	RBTreeNode(const T& data, Color col = Red)  //构造函数
		:_data(data)
		, _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _col(col)  //默认新插入节点的颜色为红色
	{ }
};

//Value决定你是k模型的set、还是KV模型的map
template<class K, class T, class KeyOfT> 
class RBTree {  
public:
	typedef RBTreeNode<T> Node;
};
template<class K>
class set{   //K模型
public:   
       
private:  //set中的key不允许被修改
		RBTree<K, const K, SetKeyOfT> _t;  //红黑树对象
	};
}
template<class K, class V>
class map {   //KV模型  
public:

private:   //map中的key不允许被修改
		RBTree<K, pair<const K, V>, MapKeyOfT> _t;  //红黑树对象
	};

};

1.1.2 KeyOfValue

  • KeyOfT : 取出Value对象中的key。

image.png

// KeyOfT : 取出Value对象中的key
template<class K, class T, class KeyOfT> 
class RBTree {  };
struct SetKeyOfT{   
	const K& operator()(const K& key)
	{
		return key;  //key
	}
};
struct MapKeyOfT {
	const K& operator()(const pair<K, V>& kv)
	{
		return kv.first;  //pair中的key
	}
};

1.2 正向迭代器

1.2.1 构造函数

💡RBTreeIterator(Node* node) ;

RBTreeIterator(Node* node) //构造函数,单参数构造函数支持隐式类型转化
		:_node(node)
	{ }
  • Tips : 单参数构造函数支持隐式类型转换 Node*->iterator 。

1.2.2 begin()+end()

💡iterator begin( ) ;

  • 功能:返回红黑树中最左节点(左孩子必为空)的迭代器。
  • Tips:set、map对象为非const对象,就调用begin()、end()。
iterator begin()  //红黑树最左节点
{ 
	Node* subLeft = _root;   
	while (subLeft && subLeft->_left)
		subLeft = subLeft->_left;

	return iterator(subLeft);
}	

💡iterator end( ) ;

  • 功能:返回最后一个元素的下一个的迭代器(空指针)。
iterator end()  //空指针 左闭右开[begin,end)
{
	return iterator(nullptr);
}

image.png

1.2.3 operator++()

/*1.右不为空,下一个为该节点右子树的最左节点 ;
	* 2.右为空,说明该节点所在的子树已经访问完了,若该节点为父亲的右,说明该父亲所在的子树也访问完了,继续往上找,
	* 直到该节点为父亲的左,则要访问的下一个节点是它的父亲,即:找祖先里面的孩纸 = 父亲左*/
Self& operator++()  //中序 左、根、右
{
	if (_node->_right)  //1
	{
		Node* subLeft = _node->_right;
		while (subLeft->_left)
			subLeft = subLeft->_left;

		_node = subLeft;
	}
	else  //2
	{
		Node* cur = _node;
		Node* parent = cur->_parent;
		while (parent && parent->_left != cur)
		{
			cur = parent;
			parent = cur->_parent;
		}
		_node = parent;
	}
	return *this;
}
  • 中序遍历,左、根、右。
    情况一:右不为空,下一个为该节点右子树的最左节点 ;
    情况二:右为空,说明该节点所在的子树已经访问完了,若该节点为父亲的右,说明该父亲所在的子树也访问完了,继续往上找,直到该节点为父亲的左,则要访问的下一个节点是它的父亲,即:找祖先里面的孩纸 = 父亲左。

image.png

1.2.4 operator–()

/*1.左不为空,下一个为该节点左子树的最右节点 ;
	* 2.左为空,说明该节点所在的子树已经访问完了,若该节点为父亲的左,说明该父亲所在的子树也访问完了,继续往上找,
	* 直到该节点为父亲的右,则要访问的下一个节点是它的父亲,即:找祖先里面的孩纸 = 父亲右*/
Self& operator--() //中序 左、根、右  --与++逻辑相反
{
	if (_node->_left)  //1
	{
		Node* subRight = _node->_left;
		while (subRight->_right)
			subRight = subRight->_right;

		_node = subRight;
	}
	else  //2
	{
		Node* cur = _node;
		Node* parent = cur->_parent;
		while (parent && parent->_right != cur)
		{
			cur = parent;
			parent = cur->_parent;
		}
		_node = parent;
	}
	return *this;
}

image.png

1.2.5 operator*()

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

1.2.6 operator->()

Ptr operator->() //结构体指针,data为结构体
{
	return &_node->_data;
}

1.2.7 operator==()

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

1.2.8 operator!=()

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

1.2.9 总代码

template<class T, class Ref, class Ptr> 
struct RBTreeIterator   //红黑树的正向迭代器
{
	typedef RBTreeNode<T> Node;
	typedef RBTreeIterator<T, Ref, Ptr> Self;

	Node* _node;

	RBTreeIterator(Node* node) //构造函数,单参数构造函数支持隐式类型转化
		:_node(node)
	{ }

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

	Ptr operator->() //结构体指针,data为结构体
	{
		return &_node->_data;
	}

	/*1.右不为空,下一个为该节点右子树的最左节点 ;
	* 2.右为空,说明该节点所在的子树已经访问完了,若该节点为父亲的右,说明该父亲所在的子树也访问完了,继续往上找,
	* 直到该节点为父亲的左,则要访问的下一个节点是它的父亲,即:找祖先里面的孩纸 = 父亲左*/
	Self& operator++()  //中序 左、根、右
	{
		if (_node->_right)  //1
		{
			Node* subLeft = _node->_right;
			while (subLeft->_left)
				subLeft = subLeft->_left;

			_node = subLeft;
		}
		else  //2
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && parent->_left != cur)
			{
				cur = parent;
				parent = cur->_parent;
			}
			_node = parent;
		}
		return *this;
	}

	/*1.左不为空,下一个为该节点左子树的最右节点 ;
	* 2.左为空,说明该节点所在的子树已经访问完了,若该节点为父亲的左,说明该父亲所在的子树也访问完了,继续往上找,
	* 直到该节点为父亲的右,则要访问的下一个节点是它的父亲,即:找祖先里面的孩纸 = 父亲右*/
	Self& operator--() //中序 左、根、右  --与++逻辑相反
	{
		if (_node->_left)  //1
		{
			Node* subRight = _node->_left;
			while (subRight->_right)
				subRight = subRight->_right;

			_node = subRight;
		}
		else  //2
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && parent->_right != cur)
			{
				cur = parent;
				parent = cur->_parent;
			}
			_node = parent;
		}
		return *this;
	}

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

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

1.3 反向迭代器

1.3.1 rbegin()+rend()

💡reverse_iterator rbegin( ) ;

  • 功能:返回红黑树中最右节点(右孩子必为空)的迭代器。
reverse_iterator rbegin()  //红黑树最右节点,因为此处反向迭代器*,是直接调用正向迭代器* [rbegin,erend)
{
	Node* subRight = _root;
	while (subRight && subRight->_right)
		subRight = subRight->_right;

	return reverse_iterator(subRight);
}

💡reverse_iterator rend( ) ;

  • 功能:返回第一个元素的前一个的迭代器(空指针)。
reverse_iterator rend()  
{
	return reverse_iterator(nullptr);
}

1.3.2 总代码

template<class iterator, class Ref, class Ptr>
struct ReverseIterator  //红黑树的反向迭代器——适配器 begin = rend、end = rbegin
{
	typedef ReverseIterator<iterator, Ref, Ptr> Self;  
	iterator _it;   //适配器

	ReverseIterator(iterator it)
		:_it(it)
	{ }

	Ref operator*()
	{
		return *_it;
	}

	Ptr operator->()
	{
		return &(operator*());  //
	}

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

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

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

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

1.4 find()

💡iterator find(const K& key) ;

  • 功能:查找。
  • 若key在红黑树中,则返回树中与key值相等元素的迭代器,否则,就返回end( )。
iterator find(const K& key)  //查找  模板参数K的作用
{
	KeyOfT kot;

	Node* cur = _root;
	while (cur)  //先按照二叉搜索树的方式插入
	{
		if (kot(cur->_data) < key)  //通仿函数对象调用operator()来获取T中的key
			cur = cur->_right;
		else if (kot(cur->_data) > key)
			cur = cur->_left;
		else
			return iterator(cur);  //找到了
	}
	return end();  //找不到
}

1.5 insert()

💡pair<iterator,bool> insert(const T& data) ;

  • 功能:向红黑树中插入data。

image.png

  • insert返回值为pair<iterator, bool>,若key(set的key、map的pair的first)在树中存在,因为搜索树中不能出现重复的键值key,所以pair::first指向在树中与key值相等的迭代器,pair::second为false。若key在树中不存在,pair::first指向在树中新插入元素的迭代器,pair::second为true。insert相当于查找。
pair<iterator, bool> insert(const T& data)  //插入  
{   //不能使用引用放回,因为返回值作用域为栈区,传值返回
	KeyOfT kot;  //仿函数类创建的对象,对象去调用operator()

	Node* parent = nullptr;
	Node* cur = _root;
	while (cur)  //先按照二叉搜索树的方式插入
    {
    	if (kot(cur->_data) < kot(data))  //通仿函数对象调用operator()来获取T中的key值
    	{
    		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);  //注意 insert
		
	Node* newnode = cur;  //非空树,插入成功,因为要做旋转处理,会导致cur值发生改变,需提前将新节点的位置存储起来

	if (parent == nullptr) //空树
	{
		_root = cur;
		_root->_col = Black;  //跟节点为黑
		return make_pair(iterator(_root), true);  //空树,插入成功,返回新插入节点在树中的迭代器
	}

	if (kot(parent->_data) < kot(cur->_data))
	{
		parent->_right = cur;
	}
	else
	{
		parent->_left = cur;
	}
	cur->_parent = parent;  //记录当前节点的父亲

	//更新颜色
	//插入结束:1.插入节点的父亲是黑色,因为插入前该树就为红黑树 2.情况一处理完后,cur为根节点,且为黑色
	while (parent && parent->_col == Red)
	{ //爷爷一定存在,因为c为红,p为红,所以p一定不是根节点,且一定有父节点
		Node* grandfather = parent->_parent;
		if (parent == grandfather->_left)  //旋转需要确定方向
		{
			Node* uncle = grandfather->_right;
			if (uncle && uncle->_col == Red) //情况一:叔叔存在且为红->无方向(p、u为g的任意边,c为p的任一边)
			{  //cur可能为新增节点,也可能一开始为黑色,cur的子树(下一层为红,下一层为新插入节点)在调整过程中将cur由黑变为红
				parent->_col = uncle->_col = Black; //p、u变为黑,g变为红
				grandfather->_col = Red;

				//g可能为根节点(更新结束),也可能为子树(继续向上更新)
				cur = grandfather;
				parent = cur->_parent;
			}
			else  //情况二:叔叔不存在 或者 叔叔存在且为黑
			{  //叔叔不存在,cur为新增节点 或 cur原来为黑,经子树调整由黑变红
				if (parent->_left == cur)  //左左——右单旋
				{
					RotateR(grandfather);
					parent->_col = Black; //p变为黑,g变为红
					grandfather->_col = Red;
				}
				else    //左右——左右单旋 
				{
					RotateL(parent);
					RotateR(grandfather);
					cur->_col = Black;  //c变黑,g变红
					grandfather->_col = Red;
				}
				break;  //更新结束:3.旋转+颜色处理后就是红黑树了
			}
		}
		else
		{
			Node* uncle = grandfather->_left;
			if (uncle && uncle->_col == Red)
			{
				parent->_col = uncle->_col = Black;
				grandfather->_col = Red;

				cur = grandfather;
				parent = cur->_parent;
			}
			else
			{
				if (parent->_right == cur)  //右右——左单旋
				{
					RotateL(grandfather);
					parent->_col = Black;
					grandfather->_col = Red;
				}
				else   //右左——右左单旋
				{
					RotateR(parent);
					RotateL(grandfather);
					cur->_col = Black;
					grandfather->_col = Red;
				}
				break;
			}
		}
	}
	_root->_col = Black;  //g为根,颜色变为黑,更新结束
	return make_pair(iterator(newnode), true);  //情况一,插入节点的父亲为黑,插入结束
}

2. Map

2.1 operator[]

💡V& operator[ ](const K& key) ;

  • 功能:访问与key相对应的value值。即可读又可写。
  • 原理:operator[ ]底层是通过调用insert( )将键值队插入到map中。如果key存在,插入失败,insert返回与map中key值相同元素的迭代器。如果key不存在,插入成功,insert返回在map中新插入元素的迭代器。operator[ ]最后返回与key值相对应的value值的引用。
  • operator[ ] 具有插入、查找、插入+修改、查找+修改功能。
V& operator[](const K& key) //功能:查找+修改、插入+修改
{
	pair<iterator, bool> ret = _t.insert(make_pair(key, V()));
	return ret.first->second;
}

3. typename作用

  1. 使用域作用限定符(: : )的两种情况:静态变量、类中typedef的类型。
  2. 使用typename表示: :后面为类型,不是静态成员
//使用::两种情况:静态变量、类中typedef的类型  typename表示::前面为类型,不是静态成员
typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::iterator iterator; 

4. 完整代码

4.1 Map.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1

#include"RBTree.h"
#include<string>

namespace zzx {
	template<class K, class V>
	class map {   //KV模型  
	public:

		struct MapKeyOfT {
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;  //pair中的key
			}
		};

		//使用::两种情况:静态变量、类中typedef的类型  typename表示::前面为类型,不是静态成员
		typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::iterator iterator;   //正向迭代器
		typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::reverse_iterator reverse_iterator;  //反向迭代器

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

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

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

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

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

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

		V& operator[](const K& key) //功能:查找+修改、插入+修改
		{
			pair<iterator, bool> ret = _t.insert(make_pair(key, V()));
			return ret.first->second;
		}

	private:   //map中的key不允许被修改
		RBTree<K, pair<const K, V>, MapKeyOfT> _t;  //红黑树对象
	};

};

4.2 Set.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1

#include"RBTree.h"

namespace zzx{
	template<class K>
	class set{   //K模型
	public:   //仿函数类:比较对象大小,获取对象中的元素—自己手动传递比较逻辑
		struct SetKeyOfT{   
			const K& operator()(const K& key)
			{
				return key;  //key
			}
		};

		//使用::两种情况:静态变量、类中typedef的类型  typename表示::后面为类型,不是静态成员
		typedef typename RBTree<K, const K, SetKeyOfT>::iterator iterator;   //正向迭代器
		typedef typename RBTree<K, const K, SetKeyOfT>::reverse_iterator reverse_iterator;  //反向迭代器

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

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

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

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

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

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

	private:  //set中的key不允许被修改
		RBTree<K, const K, SetKeyOfT> _t;  //红黑树对象
	};
}

4.3 RBTree.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>

using namespace std;

enum Color {  //枚举,一一列举出事物具有的所有可能
	Red,  //枚举常量,给枚举变量进行赋值
	Black,
};

template<class T>//红黑树的节点
struct RBTreeNode {
	typedef RBTreeNode<T> Node;

	//三叉链-》优点:便于查找孩子、父亲节点
	Node* _left;      //该节点的左孩子
	Node* _right;    //该节点的右孩子
	Node* _parent;  //该节点的父亲,便于向上更新
	T _data;
	Color _col;

	RBTreeNode(const T& data, Color col = Red)  //构造函数
		:_data(data)
		, _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _col(col)  //默认新插入节点的颜色为红色
	{ }
};

template<class T, class Ref, class Ptr> 
struct RBTreeIterator   //红黑树的正向迭代器
{
	typedef RBTreeNode<T> Node;
	typedef RBTreeIterator<T, Ref, Ptr> Self;

	Node* _node;

	RBTreeIterator(Node* node) //构造函数,单参数构造函数支持隐式类型转化
		:_node(node)
	{ }

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

	Ptr operator->() //结构体指针,data为结构体
	{
		return &_node->_data;
	}

	/*1.右不为空,下一个为该节点右子树的最左节点 ;
	* 2.右为空,说明该节点所在的子树已经访问完了,若该节点为父亲的右,说明该父亲所在的子树也访问完了,继续往上找,
	* 直到该节点为父亲的左,则要访问的下一个节点是它的父亲,即:找祖先里面的孩纸 = 父亲左*/
	Self& operator++()  //中序 左、根、右
	{
		if (_node->_right)  //1
		{
			Node* subLeft = _node->_right;
			while (subLeft->_left)
				subLeft = subLeft->_left;

			_node = subLeft;
		}
		else  //2
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && parent->_left != cur)
			{
				cur = parent;
				parent = cur->_parent;
			}
			_node = parent;
		}
		return *this;
	}

	/*1.左不为空,下一个为该节点左子树的最右节点 ;
	* 2.左为空,说明该节点所在的子树已经访问完了,若该节点为父亲的左,说明该父亲所在的子树也访问完了,继续往上找,
	* 直到该节点为父亲的右,则要访问的下一个节点是它的父亲,即:找祖先里面的孩纸 = 父亲右*/
	Self& operator--() //中序 左、根、右  --与++逻辑相反
	{
		if (_node->_left)  //1
		{
			Node* subRight = _node->_left;
			while (subRight->_right)
				subRight = subRight->_right;

			_node = subRight;
		}
		else  //2
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && parent->_right != cur)
			{
				cur = parent;
				parent = cur->_parent;
			}
			_node = parent;
		}
		return *this;
	}

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

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

template<class iterator, class Ref, class Ptr>
struct ReverseIterator  //红黑树的反向迭代器——适配器 begin = rend、end = rbegin
{
	typedef ReverseIterator<iterator, Ref, Ptr> Self;  
	iterator _it;   //适配器

	ReverseIterator(iterator it)
		:_it(it)
	{ }

	Ref operator*()
	{
		return *_it;
	}

	Ptr operator->()
	{
		return &(operator*());  //
	}

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

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

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

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

//红黑树的模板参数:T决定你是k模型的set、还是KV模型的map ; KeyOfT:取出T对象中的key ; pair比较:先比较first,在比较second
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*> const_iterator; //const迭代器

	//反向迭代器
	typedef ReverseIterator<iterator, T&, T*> reverse_iterator;
	typedef ReverseIterator<const_iterator, const T&, const T*> const_reverse_iterator;

	iterator begin()  //红黑树最左节点
	{ 
		Node* subLeft = _root;   
		while (subLeft && subLeft->_left)
			subLeft = subLeft->_left;

		return iterator(subLeft);
	}

	iterator end()  //空指针 左闭右开[begin,end)
	{
		return iterator(nullptr);
	}

	const_iterator begin()const  
	{
		Node* subLeft = _root;
		while (subLeft && subLeft->_left)
			subLeft = subLeft->_left;

		return const_iterator(subLeft);
	}

	const_iterator end()const
	{
		return const_iterator(nullptr);
	}

	reverse_iterator rbegin()  //红黑树最右节点,因为此处反向迭代器*,是直接调用正向迭代器* [rbegin,erend)
	{
		Node* subRight = _root;
		while (subRight && subRight->_right)
			subRight = subRight->_right;

		return reverse_iterator(subRight);
	}

	reverse_iterator rend()  
	{
		return reverse_iterator(nullptr);
	}

	const_reverse_iterator rbegin()const
	{
		Node* subRight = _root;
		while (subRight && subRight->_right)
			subRight = subRight->_right;

		return const_reverse_iterator(subRight);
	}

	const_reverse_iterator rend()const
	{
		return const_reverse_iterator(nullptr);
	}

	iterator find(const K& key)  //查找  模板参数K的作用
	{
		KeyOfT kot;

		Node* cur = _root;
		while (cur)  //先按照二叉搜索树的方式插入
		{
			if (kot(cur->_data) < key)  //通仿函数对象调用operator()来获取T中的key
				cur = cur->_right;
			else if (kot(cur->_data) > key)
				cur = cur->_left;
			else
				return iterator(cur);  //找到了
		}

		return end();  //找不到
	}

	void RotateL(Node* parent)  //右右—左单旋
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		Node* pphead = parent->_parent;

		parent->_right = subRL;
		if (subRL)
			subRL->_parent = parent;
		subR->_left = parent;
		parent->_parent = subR;


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

			subR->_parent = pphead;
		}
	}

	void RotateR(Node* parent)  //左左—右单旋
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		Node* pphead = parent->_parent;

		parent->_left = subLR;
		if (subLR)
			subLR->_parent = parent;
		subL->_right = parent;
		parent->_parent = subL;

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

			subL->_parent = pphead;
		}
	}

	void RotateRL(Node* parent)  //右左—先右旋再左旋
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		RotateR(subR);
		RotateL(parent);
	}

	void RotateLR(Node* parent)  //左右—先左旋再右旋
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		RotateL(subL);
		RotateR(parent);
	}

	pair<iterator, bool> insert(const T& data)  //插入  
	{   //不能使用引用放回,因为返回值作用域为栈区,传值返回
		KeyOfT kot;  //仿函数类创建的对象,对象去调用operator()

		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)  //先按照二叉搜索树的方式插入
		{
			if (kot(cur->_data) < kot(data))  //通仿函数对象调用operator()来获取T中的key值
			{
				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);  //注意 insert
		
		Node* newnode = cur;  //非空树,插入成功,因为要做旋转处理,会导致cur值发生改变,需提前将新节点的位置存储起来

		if (parent == nullptr) //空树
		{
			_root = cur;
			_root->_col = Black;  //跟节点为黑
			return make_pair(iterator(_root), true);  //空树,插入成功,返回新插入节点在树中的迭代器
		}

		if (kot(parent->_data) < kot(cur->_data))
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}
		cur->_parent = parent;  //记录当前节点的父亲

		//更新颜色
		//插入结束:1.插入节点的父亲是黑色,因为插入前该树就为红黑树 2.情况一处理完后,cur为根节点,且为黑色
		while (parent && parent->_col == Red)
		{ //爷爷一定存在,因为c为红,p为红,所以p一定不是根节点,且一定有父节点
			Node* grandfather = parent->_parent;
			if (parent == grandfather->_left)  //旋转需要确定方向
			{
				Node* uncle = grandfather->_right;
				if (uncle && uncle->_col == Red) //情况一:叔叔存在且为红->无方向(p、u为g的任意边,c为p的任一边)
				{  //cur可能为新增节点,也可能一开始为黑色,cur的子树(下一层为红,下一层为新插入节点)在调整过程中将cur由黑变为红
					parent->_col = uncle->_col = Black; //p、u变为黑,g变为红
					grandfather->_col = Red;

					//g可能为根节点(更新结束),也可能为子树(继续向上更新)
					cur = grandfather;
					parent = cur->_parent;
				}
				else  //情况二:叔叔不存在 或者 叔叔存在且为黑
				{  //叔叔不存在,cur为新增节点 或 cur原来为黑,经子树调整由黑变红
					if (parent->_left == cur)  //左左——右单旋
					{
						RotateR(grandfather);
						parent->_col = Black; //p变为黑,g变为红
						grandfather->_col = Red;
					}
					else    //左右——左右单旋 
					{
						RotateL(parent);
						RotateR(grandfather);
						cur->_col = Black;  //c变黑,g变红
						grandfather->_col = Red;
					}
					break;  //更新结束:3.旋转+颜色处理后就是红黑树了
				}
			}
			else
			{
				Node* uncle = grandfather->_left;
				if (uncle && uncle->_col == Red)
				{
					parent->_col = uncle->_col = Black;
					grandfather->_col = Red;

					cur = grandfather;
					parent = cur->_parent;
				}
				else
				{
					if (parent->_right == cur)  //右右——左单旋
					{
						RotateL(grandfather);
						parent->_col = Black;
						grandfather->_col = Red;
					}
					else   //右左——右左单旋
					{
						RotateR(parent);
						RotateL(grandfather);
						cur->_col = Black;
						grandfather->_col = Red;
					}
					break;
				}
			}
		}
		_root->_col = Black;  //g为根,颜色变为黑,更新结束
		return make_pair(iterator(newnode), true);  //情况一,插入节点的父亲为黑,插入结束
	}

private:
	Node* _root = nullptr;
};

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

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

相关文章

MySQL——C语言连接数据库

MySQL Connection ​ 连接数据库的客户端除了命令行式的还有图形化界面版本&#xff0c;网页版本&#xff0c;当然也包括语言级别的库或者是包&#xff0c;能够帮助我们直接连接数据库&#xff1b; 一、语言连接库下载 方式一&#xff1a;不建议使用&#xff0c;需要自己配置…

一文搞懂大模型训练加速框架 DeepSpeed 的使用方法!

节前&#xff0c;我们星球组织了一场算法岗技术&面试讨论会&#xff0c;邀请了一些互联网大厂朋友、参加社招和校招面试的同学。 针对算法岗技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何准备、面试常考点分享等热门话题进行了深入的讨论。 合集&#x…

Vuepress 2从0-1保姆级进阶教程——标准化流程

Vuepress 2 专栏目录 1. 入门阶段 Vuepress 2从0-1保姆级入门教程——环境配置篇Vuepress 2从0-1保姆级入门教程——安装流程篇Vuepress 2从0-1保姆级入门教程——文档配置篇Vuepress 2从0-1保姆级入门教程——范例与部署 2.进阶阶段 Vuepress 2从0-1保姆级进阶教程——全文搜索…

工具篇之NATAPP实现内网穿透

一、内网穿透 1.1概述 内网穿透简单来说就是我们可以通过在个人电脑上运行花生壳或者 frp 等方式&#xff0c;让他人访问我们本地启动的服务&#xff0c;而且这种访问可以不受局域网的限制。比如常用的办公室软件等&#xff0c;一般在办公室或家里&#xff0c;通过拨号上网&am…

Ubuntu项目部署

解压jdk tar -zxvf jdk-8u151-linux-x64.tar.gz 配置Java环境变量&#xff1a; vim ~/.bashrc export JAVA_HOME/root/soft/jdk1.8.0_151 export JRE_HOME${JAVA_HOME}/jre export CLASSPATH.:${JAVA_HOME}/lib:${JRE_HOME}/lib export PATH${JAVA_HOME}/bin:$PATH 设置环境变…

workerman error 2 send buffer full and drop package

来源 报错信息&#xff1a;workerman error 2 send buffer full and drop package 定时发送数据的时候&#xff0c;本地偶尔出现这种情况 线上第一条数据发出去就报错了&#xff0c;数据改小一点可以发&#xff0c;不过一会还是会出现这种情况。 解决 根据我的经验&#xf…

基于comsol进行等离子体缺陷的二维微结构电磁调制仿真

关键词&#xff1a;微结构器件&#xff1b;禁带效应&#xff1b;等离子体缺陷&#xff1b;开关调控&#xff1b;电磁波调制 光子晶体是一种介电常数呈周期变化的材料&#xff0c;通常通过调节介质材料与空气或其他具有折射率差异材料间的周期排列结构&#xff0c;实现电磁波透…

问题:11单位内部人员对行政机关作出的行政处分不服,可申请行政复议. #其他#微信

问题&#xff1a;11单位内部人员对行政机关作出的行政处分不服,可申请行政复议. 参考答案如图所示

Java Web学习笔记5——基础标签和样式

<!DOCTYPE html> html有很多版本&#xff0c;那我们应该告诉用户和浏览器我们现在使用的是HMTL哪个版本。 声明为HTML5文档。 字符集&#xff1a; UTF-8&#xff1a;现在最常用的字符编码方式。 GB2312&#xff1a;简体中文 BIG5&#xff1a;繁体中文、港澳台等方式…

烧写uboot、linux镜像、根文件系统到开发板

烧写uboot、linux镜像、根文件系统到开发板 环境介绍 本博客使用x6818开发板。 公司&#xff1a;三星 ARM架构 Cortex-A53核 型号&#xff1a;S5P6818 特性&#xff1a;8核&#xff0c;最高主频2GHz 烧写uboot 使用网络烧写 网络烧写上位机是Ubuntu虚拟机。 先利用上…

Lidar3607.2 雷达点云数据处理软件新增功能介绍

新特性:预处理航带平差新增livox激光器镜面误差改正,新增多源航带平差&#xff0c;提升点云和影像匹配精度优化配准功能流程&#xff0c;ICP功能支持点云与模型配准安置检校新增轨迹自动裁剪轨迹解算时投影坐标增加Z值记录数据管理新增点云色彩亮度和对比度调节新增多段线平滑工…

【多模态/CV】图像数据增强数据分析和处理

note 多模态大模型训练前&#xff0c;图片数据处理的常见操作&#xff1a;分辨率调整、网格畸变、水平翻转、分辨率调整、随机crop、换颜色、多张图片拼接、相似图片检测并去重等 一、分辨率调整 from PIL import Image def resize_image(original_image_path, save_image_p…

机器学习与数据挖掘知识点总结(一)

简介&#xff1a;随着人工智能&#xff08;AI&#xff09;蓬勃发展&#xff0c;也有越来越多的人涌入到这一行业。下面简单介绍一下机器学习的各大领域&#xff0c;机器学习包含深度学习以及强化学习&#xff0c;在本节的机器学习中主要阐述一下机器学习的线性回归逻辑回归&…

【JavaScript函数详解】Day04

JavaScript函数详解 JavaScript 基础 - 第4天笔记函数声明和调用声明&#xff08;定义&#xff09;调用 参数形参和实参参数默认值 返回值函数补充细节作用域全局作用域局部作用域变量的访问原则 匿名函数函数表达式立即执行函数 逻辑中断小知识&#xff08;转换为Boolean型&am…

WPF视频学习-基础知识篇

1.简介WPF&#xff1a; C# 一套关于windows界面应用开发框架 2.WPF和winform的差别 &#xff0c;(WPF比较新) 创建新项目使用模板&#xff1a; WPF使用.xaml后缀&#xff0c;双击可查看操作界面和设置代码&#xff0c;其文件展开之后中有MainWindow.xaml.cs为程序交互逻辑。…

Vitis HLS 学习笔记--初始化与复位

目录 1. 简介 2. 控制初始化与复位 2.1 初始化 2.2 复位 2.3 全局复位选项 2.4 复位排除 3. 阵列初始化和复位 3.1 不使用 static 限定符 3.2 使用 static 限定符 3.3 BRAM 和 URAM 4. 总结 1. 简介 本文对比分析两个方面的初始化和复位&#xff1a;阵列和控制&…

如何检测UV胶的均匀性?

如何检测UV胶的均匀性&#xff1f; 检测UV胶的均匀性可以通过以下几种方法来实现&#xff1a; 肉眼目视检查&#xff1a; 这是最简单直接的方法。将UV胶涂在表面上&#xff0c;使用裸眼观察胶层的表面。特别注意是否存在气泡、颜色不均匀、裂纹或其他明显的不均匀性。如凹凸不…

选择排序(直接选择排序与堆排序)----数据结构-排序②

1、选择排序 1.1 基本思想 每一次从待排序的数据元素中选出最小&#xff08;或最大&#xff09;的一个元素&#xff0c;放在序列的起始位置&#xff0c;直到全部待排序的数据元素排完就停止 。 1.2 直接选择排序 排序思想&#xff1a; ①在元素集合array[i]--array[n-1]中选择…

FM148A,FM146B运行备件

FM148A,FM146B运行备件。电源保险丝仓主控底座的保险丝仓示意图底座上共有两个保险丝&#xff08;800mA&#xff09;&#xff0c;FM148A,FM146B运行备件。&#xff08;10&#xff5e;73&#xff09;30/195主控单元2.K-CUT014槽底座地址接口主控站地址拨开关从上到下为二进制数的…

Day46 代码随想录打卡|二叉树篇---从中序与后序遍历序列构造二叉树

题目&#xff08;leecode T106&#xff09;&#xff1a; 给定两个整数数组 inorder 和 postorder &#xff0c;其中 inorder 是二叉树的中序遍历&#xff0c; postorder 是同一棵树的后序遍历&#xff0c;请你构造并返回这颗 二叉树 。 方法&#xff1a;本题要通过中序遍历和后…