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

用红黑树封装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.mfbz.cn/a/692277.html

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

相关文章

vue2的element的table组件使用form校验

1.需求描述 vue2有时候做自增表格el-table&#xff0c;希望能够带一些校验&#xff0c;但又不想手搓校验逻辑&#xff0c;可以借用el-form的校验逻辑。 2.代码处理 1. html <template><div class"sad-cont"><el-form ref"form" :model&…

WPF视频学习-基础知识篇

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

1.Vue2使用ElementUI-初识及环境搭建

目录 1.下载nodejs v16.x 2.设置淘宝镜像源 3.安装脚手架 4.创建一个项目 5.项目修改 代码地址&#xff1a;source-code: 源码笔记 1.下载nodejs v16.x 下载地址&#xff1a;Node.js — Download Node.js 2.设置淘宝镜像源 npm config set registry https://registry.…

Python实现连连看9

&#xff08;2&#xff09;标识选中的图片 在判断出玩家选中的是哪一张图片之后&#xff0c;接下来就可以标识选中的图片了&#xff0c;即在该选中的图片外围画矩形。代码如下所示。 FIRSTCLICK True #FIRSTCLICK是全局变量 if(click_col>0 and click_row>0) and \(no…

GUI编程-01

组件 窗口 弹窗 面板 文本框 列表框 按钮 图片 监听事件 鼠标 键盘事件 破解工具 Java提供了丰富的图形用户界面&#xff08;Graphics User Interface&#xff0c;GUI&#xff09;的类库&#xff0c;基于这些类库可以编写窗口程序。 Java关于图形界面的类库主要放在…

uniapp自定义的下面导航

uniapp自定义的下面导航 看看效果图片吧 文章目录 uniapp自定义的下面导航 看看效果图片吧 ![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/6aa0e964741d4dd3a58f4e86c4bf3247.png) 前言一、写组件、我这里就没有写组件了直接写了一个页面&#xff1f;总结 前言 在…

JWT 快速入门

什么是 JWT JSON Web Token&#xff08;JWT&#xff09;是目前最流行的跨域身份验证解决方案 JSON Web Token Introduction - jwt.ioLearn about JSON Web Tokens, what are they, how they work, when and why you should use them.https://jwt.io/introduction 一、常见会…

Vue13-计算属性的简写

一、计算属性的简写 注意&#xff1a; 当计算属性只有get&#xff0c;没有set的时候&#xff0c;才能用简写形式&#xff01;&#xff01;&#xff01;

端午与高考的交汇点:家的温暖与梦想的起点

当端午节的粽香弥漫在街头巷尾&#xff0c;高考的脚步也悄然而至。这两个看似毫无关联的时刻&#xff0c;却在每年的六月&#xff0c;奇妙地交汇在一起&#xff0c;为我们带来了一段特别的记忆。这不仅是家的温暖与梦想的起点相遇的时刻&#xff0c;更是传统文化与现代追求共融…

Rust-06-所有权

所有权&#xff08;系统&#xff09;是 Rust 最为与众不同的特性&#xff0c;它让 Rust 无需垃圾回收即可保障内存安全&#xff0c;下面是所有权以及相关功能&#xff1a;借用&#xff08;borrowing&#xff09;、slice 以及 Rust 如何在内存中布局数据。 通过所有权系统管理内…

Java版工程项目管理平台:以源码驱动,引领工程企业数字化转型

在当今数字化时代&#xff0c;随着企业的扩张和业务的增长&#xff0c;传统的工程项目管理方法已显不足。为了提升管理效率、减轻工作负担、增强信息处理的快速性和精确度&#xff0c;工程企业亟需借助数字化技术进行转型升级。本文将向您展示一款基于Spring Cloud、Spring Boo…

当前主流的App开发技术综述

一、引言 随着移动互联网的蓬勃发展&#xff0c;App&#xff08;应用程序&#xff09;已经成为人们日常生活中不可或缺的一部分。无论是社交、购物、娱乐还是工作学习&#xff0c;App都以其便捷、高效和个性化的特点深受用户喜爱。而在这一过程中&#xff0c;App开发技术也在不…

MFC 教程-文本框失去焦点处理

【1】增加窗口的消息处理函数 void CTESTMFCDlg::OnKillFocus(CWnd* pNewWnd) {//CDialogEx::OnKillFocus(pNewWnd);//首先使用true将数据从控件传入成员变量中UpdateData(true);//校验成员变量m_data中的数据是否符合要求&#xff0c;如果不符合&#xff0c;修改后将它显示在控…

访问网站时IP被阻止?原因及解决方法

在互联网上&#xff0c;用户可能会面临一个令人困扰的问题——当尝试访问某个特定的网站时&#xff0c;却发现自己的IP地址被该网站屏蔽。 IP地址被网站屏蔽是一个相对常见的现象&#xff0c;而导致这种情况的原因多种多样&#xff0c;包括恶意行为、违规访问等。本文将解释IP地…

转让北京公司带旅行许可证流程和要求

旅行社是开展旅游服务业务的专项经济主体&#xff0c;旅行社开展相关业务必须持有旅行社业务经营许可证。该资质又分为国内旅行社经营许可证和出境旅行社经营许可证。主要区别在于能否开展出境旅游业务&#xff0c;下面老耿带大家了解&#xff0c;新成立国内旅行社要求及出境旅…

Git之解决重复输入用户名和密码(三十九)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

【CS.CN】深入探讨下HTTP的Connection头:通过keep-alive实现高效网络连接

文章目录 0 序言0.1 由来0.2 使用场景0.3 现在还需要吗&#xff1f; 1 Connection: keep-alive的机制2 语法 && 通过设置Connection: keep-alive优化性能3 验证与性能提升4 总结References 0 序言 0.1 由来 Connection头部字段在HTTP/1.1中被引入&#xff0c;主要用于…

React的useState的基础使用

import {useState} from react // 1.调用useState添加状态变量 // count 是新增的状态变量 // setCount 修改状态变量的方法 // 2.添加点击事件回调 // userState实现计数实例import {useState} from react// 使用组件 function App() {// 1.调用useState添加状态变量// coun…

ChatTTS增强版V2,批量导出srt,语速控制,情感控制,支持朗读数字,问题修复

ChatTTS增强版最新版本已经发布&#xff0c;本次更新我主要增加了多文本批量、SRT导出、语速控制、情感控制、停顿控制等新功能&#xff0c;并针对上一版本中存在的数字读音异常、随机uv_break等问题进行了修复。 视频版本 【ChatTTS增强版V2&#xff0c;批量导出srt&#xff…

基于C#的计算机与安捷伦34970A通信方法

概述 安捷伦34970A采集数据&#xff0c;34970A支持RS232接口&#xff0c;但是如果直接用winform自带的seriaport类基本是没必要使用的&#xff0c;安捷伦等仪表通讯需要用到VISA的库。 库的获取 1. 是德科技的IO Library. 2. NI下载NI-VISA. 两者用法接近. 代码如下 using…