C++ 红黑树的封装

news2024/12/23 14:25:56

一.map/set的封装

  在实现了红黑树的部分功能后,我们可以便可以将红黑树作为底层结构来封装map 和 set ,但是问题也随之而来。我们都知道map是k-v的数据模型,而set是k的数据模型,我们难道要去使用两棵红黑树来封装吗?显然不是这样的。

  接下来我们将使用同一棵红黑树实现 map和set。

1.1 封装的基本思路 

  因为我们是用一棵红黑树去封装两种容器,使用我们的这棵红黑树,使用我们的这棵树就不能像以前一样写死成k-v模型的数据结构,需要稍作修改。

  因为红黑树不能自己判断其是否是map/set类型,我们能做的努力就是提高其适应性,对红黑树的要求为:

    既能适配 K-V模型的map,又能适配出K 模型的set。 

对map和set的要求为:

    需要准确的传入数据,传入时让红黑树只能适配出一个类型的容器,对不需要的部分进行切割。

1.2 红黑树节点的调整

  这是我们之前写出的红黑树节点,现在来看显然不太合适,因为我们已经把K-V模型写死了,不太好适配出K模型的set。

  在这里,我们建议让pair类型变成 一个模糊的 T类型,至于是pair 类型还是 k类型,我们希望让map和set来准确的传入。

1.3 map 和 set 的定义 

  我们该如何适配map和set的底层,来让其匹配红黑树呢?

  在这里,我建议让其内部的模板,暂时先有两个数据类型,第一个为 K 模型,第二个为 T模型,也就是说 当为map时,传入map<K,K>时,底层实际传入RBTree<k,pair<k,k>>,传入set<k>时,底层实际传入RBTree<K,K>。

  这样有什么好处呢? 

   为因为我们的底层实现是红黑树,那么我们为了适应map又适应set,实际传入的代表数据参数至少得是两个,可能会有些人问:“map可以直接传入pair啊,那么为什么不直接传入一个RBTree<pair<K,V>>呢?。

  因为有find的存在,map的find是按照k来进行查找的,如果直接传入pair类型,那么红黑树里面的实际类型只有一个pair,当我们查找时,根本无法按照key类型来查找,所以我们的红黑树至少得有两个模板参数。

  因此,即使set是k模型,但是在这里的底层结构上,我们仍然需要传入两个k类型。

map和set基本定义如下:
 

#pragma once
#include"RBTree.h"

using namespace MyRBTree;

namespace MySet
{
  template<class K>
  class Set
  {
  private:
	  RBTree<K, K> _set;
  };
} 
#pragma once
#include"RBTree.h"

using namespace MyRBTree;

namespace MyMap
{
	template<class K,class V>
	class Map
	{
	private:
		RBTree<K, pair<K,V>> _map;
	};
}

1.4 仿函数 KeyOfValue

  在insert中,我们经常会用到data进行一些比较,但是我们现在data的类型不固定,如果是map中的pair类型,我们就应该用其first进行比较,如果是k类型,我们就应该直接比较。

  最好的解决办法是,在map和set内部分别定义一个KetOfValue 仿函数,然后将其传入红黑树模板中,因为其传入的内容不同,所以我们用来分别处理不同的数据。

  如果是map中的data,因为是kv结构,所以我们取出其data.first;如果是set中的k模型,那么我们直接返回,仿函数定义如下:
  

#pragma once
#include"RBTree.h"
using namespace MyRBTree;

namespace MyMap
{
	template<class K,class V>
	class Map
	{
	public:
		struct MapKeyOfvalue
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};
	private:
		RBTree<K, pair<K,V>, MapKeyOfvalue> _map;
	};
}
#pragma once
#include"RBTree.h"

using namespace MyRBTree;

namespace MySet
{
  template<class K>
  class Set
  {
  public:
	  struct SetKeyOfvalue
	  {
		  const K& operator()(const K& key)
		  {
			  return key;
		  }
	  };
  private:
	  RBTree<K, K, SetKeyOfvalue> _set;
  };
} 

同时我们因为要多传入一个仿函数,所以我们的模板和map,set内部的红黑树模板参数也要进行修改:

同时,我们在红黑树内部也需要对进行数据比对的函数进行修改,比如说insert和find等等。

	bool Insert(const pair<K, V>& data)
	{
		//因为需要使用仿函数的机会不多,我们在需要用到的地方局部创建一下就可以了
		//如何大多数函数都需要用到,建议定义为全局变量
		KeyOfValue kot;
		if (!_root)
		{
			_root = new Node(data);
			_root->_co = Black;
			return true;
		}

		Node* cur = _root;
		Node* parent = nullptr;

		while (cur)
		{
			
			//if (cur->_data.first < data.first)
			if (kot(cur->_data)< kot(data))
			{
				parent = cur;
				cur = cur->_right;
			}
			//else if (cur->_data.first > data.first)
			else if (kot(cur->_data) > kot(data.first))
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return false;
			}
		}

		cur = new Node(data);
		cur->_co = Red;
		//if (parent->_data.first < cur->_data.first)
		if (kot(parent->_data) < kot(cur->_data))
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}
		cur->_parent = parent;

		//开始判断是否需要变色
		while (parent && parent->_co == Red)
		{
			Node* grand = parent->_parent;
			if (parent == grand->_left)
			{
				Node* uncle = grand->_right;
				if (uncle && uncle->_co == Red)
				{
					//这里只变色就好
					parent->_co = uncle->_co = Black;
					grand->_co = Red;

					cur = grand;
					parent = cur->_parent;
				}
				else
				{
					if (cur == parent->_left)
					{
						_RotateR(grand);

						grand->_co = Red;
						parent->_co = Black;
					}
					else
					{
						_RotateL(parent);
						_RotateR(grand);

						cur->_co = Black;
						grand->_co = Red;
					}
					break;
				}
			}
			else
			{
				Node* uncle = grand->_left;
				if (uncle && uncle->_co == Red)
				{
					//这里只变色就好
					parent->_co = uncle->_co = Black;
					grand->_co = Red;

					cur = grand;
					parent = cur->_parent;
				}
				else
				{
					if (cur == parent->_right)
					{
						_RotateL(grand);

						grand->_co = Red;
						parent->_co = Black;
					}
					else
					{
						_RotateR(parent);
						_RotateL(grand);
						cur->_co = Black;
						grand->_co = Red;
					}
					break;
				}
			}
		}
		_root->_co = Black;
		return true;
	}
	Node* Find(const K& key)
	{
		KeyOfValue kot;
		Node* cur = _root;
		while (cur)
		{
			//if (cur->_data.first < key)
			if (kot(cur->_data) < key)
			{
				cur = cur->_right;
			}
			//else if (cur->_data.first > key)
			else if (kot(cur->_data) > key)
			{
				cur = cur->_left;
			}
			else
			{
				return cur;
			}
		}
		return nullptr;
	}

1.5 map/set的插入

  现在,在其内部直接套用红黑树的插入即可。

namespace MySet
{
  template<class K>
  class Set
  {
  public:
	  struct SetKeyOfvalue
	  {
		  const K& operator()(const K& key)
		  {
			  return key;
		  }
	  };
	  bool insert(const K& k)
	  {
		  return _set.Insert(k);
	  }

  private:
	  RBTree<K, K, SetKeyOfvalue> _set;
  };
} 
namespace MyMap
{
	template<class K,class V>
	class Map
	{
	public:
		struct MapKeyOfvalue
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};
		bool insert(const pair<K,V> &kv)
		{
			return _map.Insert(kv);
		}
	private:
		RBTree<K, pair<K,V>, MapKeyOfvalue> _map;
	};
}

 在map和set中先插入一些数据来看一下有没有更改错误:

这里建议在map和set内部封装进去红黑树的Inorder打印一下检查一下错误。

void Inorder()
{
	_map.Inorder();
}

//判断一下是否平衡

void IsBalance()
{
	_map.IsBalance();
}

同时我们的Inorder函数也需要更改

void _Inorder(Node* root)
{
	if (!root)
	{
		return;
	}
	//这里我们也要更改一下
	_Inorder(root->_left);
	//cout << root->_data.first << ":" << endl;
	cout <<kot(root->_data) <<" ";
	_Inorder(root->_right);
}

验证一下map和set的准确性:

#include"MyMap.h"
#include"MySet.h"

void testmap()
{
	MyMap::Map<int, int> _m;
	_m.insert(make_pair(1, 1));
	_m.insert(make_pair(2, 1));
	_m.insert(make_pair(3, 1));
	_m.insert(make_pair(4, 1));
	_m.insert(make_pair(5, 1));
	_m.Inorder();
	_m.IsBalance();
	cout << endl << endl;
}

void testset()
{
	MySet::Set<int> _s;
	_s.insert(1);
	_s.insert(2);
	_s.insert(3);
	_s.insert(4);
	_s.insert(5);
	_s.Inorder();
	_s.IsBalance();
	cout << endl<<endl;
}

int main()
{
	testmap();
	testset();
	return 0;
}

结果为:

二. map和set迭代器的实现 

  首先,我们要明白,map/set只是相当于一层壳子,真正的底层实现永远都在红黑树的部分,迭代器也是同理,我们撰写的迭代器应该书写在红黑树部分。

  迭代器的基本定义:

	//	   节点数据	 引用/const引用  指针/const指针
	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)
		{}
	};

2.1 解引用运算符重载

  一般用来取出其节点中存储的数据,直接返回data即可,并且一般而言是是可以修改的,因此这里我们返回其引用。

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

2.2 成员访问运算符重载

  -> 运算符一般用来返回该节点的地址,主要还是适用于map中,用来访问pair里面的first和second成员。

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

2.3 ==  和 != 运算符重载

这个就比较简单了

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


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

 2.4 begin()和end()

  迭代器常用成员函数begin()与end(),其中begin()对应红黑树的最左节点,end()对应最后一个节点的下一个节点,即nullptr(这里我们并没有设置哨兵位,感兴趣的同学可以自己研究一下)

 
iterator begin()
{
	Node* left = _root;
	while (left && left->_left)
	{
		left = left->_left;
	}
	return iterator(left);
}
 
iterator end()
{
	return iterator(nullptr);
}

 

2.6 ++ 运算符重载

  首先我们要知道,红黑树也是一个二叉搜索树,我们对其的遍历顺序也是按照中序遍历,因此,++运算符在这里指的是,中序遍历的下一个节点。

  那我们该如何找寻中序遍历的下一个节点呢。

  我们先来观察一部分红黑树的遍历情况:

当it指向17 时,++实际上是去访问18这个节点;(17有右子树)

当it访问18时,++实际上是去访问33这个节点;(18无右子树,故向上遍历)

当it访问33时,++实际上是去访问37这个节点;(33有右子树)

当it访问37时,++实际上是去访问42这个节点;(37无右子树,向上遍历)

 我们可以看出,实际上++始终围绕着右子树进行移动:

1.当所在节点有右子树时,++访问右子树的最左节点

2.当所在节点没有右子树时,++ 找孩子不是父亲右节点的祖先(这里需要进行遍历)

故得到代码:
 

self& operator++() //迭代器++,依旧返回迭代器
{
	//如果右子树存在
	if (_node->_right)
	{
		Node* left = _node->_right;
		//则寻找右子树的最左节点
		while (left->_left)
		{
			left = left->_left;
		}
		_node = left;
	}
	//如果右子树不存在
	else
	{
		//找孩子不是父亲右节点的节点
		Node* parent = _node->_parent;
		Node* cur = _node; 
		//一定要先判断parent是否存在,以此来避免越界
		while (parent&&cur == parent->_right)
		{
			cur = cur->_parent;
			parent = parent->_parent;
		}
		_node = parent;
	}
	return *this;
}

		//迭代器后置++
		self operator++(int)
		{
			self it(_node);
			++(*this);
			return it;
		}

 

 2.7 --运算符重载

  --就是++ 代码思路反过来。

  1. 左子树不为空,进行 -- 则是指向左子树(最右节点)。
  2. 左子树为空,-- 找孩子不是父亲左节点的祖先(循环查找)

得到代码如下:

	self& operator--()
	{
		//如果左子树存在
		if (_node->left)
		{
			//找左子树的最右节点
			Node* right = _node->_left;
			while (right->_right)
			{
				right = right->_right;
			}
			_node = rihgt;
		}
		//如果左子树不存在
		else
		{
			//找孩子不是父亲左节点的节点
			Node* parent = _node->parent;
			Node* cur = _node;
			while (parent&&parent->_left == cur)
			{
				cur = cur->_parent;
				parent = parent->_parent;
			}
			_node = parent;
		}
		return *this;
	}
	//后置--
	self operator--(int)
	{
		self it(_node);
		--(*this);
		return it;
	}

迭代器在map和set中的封装如下:


 

 

三 map的[] 下标访问运算符重载

我们可以看到其返回为,mapped_type类型,这是什么呢?

map文档中有如下声明:
 

翻译一下我们得知,在map中定义为其第二个模板参数T,(我们这里为V)的别名

在官方库里面,对这个【】是这样实现的:

 太迷茫了,我们拆解来看一下:

里面的第一层为:
​​​​​​​

  可见就是一层简单的插入 ,第一个传入的是k的值,第二个是我们map中第二个模板参数V的默认构造。

 第二步,将this指针指向insert返回的这个内容。

第三步, 取出this指向的内容中的first内容,那么问题来了,我们自己定义的insert返回的内容是bool类型,我们该取这个的first吗,显然不是的,让我们看看库中的insert函数。

 库中的返回值是一个pair类型,其内部分别是一个i迭代器和bool类型,那么这样的作法我们也就理解了,现在我们要修改一下insert。

 set中虽然用不到【】但其内部和set是一样定义的。

 

更改结果如下(RBTree内)

typedef _RBTreeIterator<T, T&, T*> iterator;
typedef _RBTreeIterator<T, const T&, const T*> const_iterator;


iterator begin()
{
	Node* left = _root;
	while (left && left->_left)
	{
		left = left->_left;
	}
	return iterator(left);
}

iterator end()
{
	return iterator(nullptr);
}


//bool Insert(const T& data)
pair<iterator, bool> Insert(const T& data)
{
	if (!_root)
	{
		_root = new Node(data);
		_root->_co = Black;
		//如果成功,返回成功节点的迭代器和true
		return make_pair(iterator(_root), true);
	}

	Node* cur = _root;
	Node* parent = nullptr;

	while (cur)
	{
		
		//if (cur->_data.first < data.first)
		if (kot(cur->_data)< kot(data))
		{
			parent = cur;
			cur = cur->_right;
		}
		//else if (cur->_data.first > data.first)
		else if (kot(cur->_data) > kot(data))
		{
			parent = cur;
			cur = cur->_left;
		}
		else
		{
			//return false;
				return make_pair(iterator(cur), false);
		}
	}

	cur = new Node(data);
	cur->_co = Red;
	//注意这里cur可能因为旋转,改变了原来的位置,所以需要提前记录一下
	Node* newnode = cur;

	//if (parent->_data.first < cur->_data.first)
	if (kot(parent->_data) < kot(cur->_data))
	{
		parent->_right = cur;
	}
	else
	{
		parent->_left = cur;
	}
	cur->_parent = parent;

	//开始判断是否需要变色
	while (parent && parent->_co == Red)
	{
		Node* grand = parent->_parent;
		if (parent == grand->_left)
		{
			Node* uncle = grand->_right;
			if (uncle && uncle->_co == Red)
			{
				//这里只变色就好
				parent->_co = uncle->_co = Black;
				grand->_co = Red;

				cur = grand;
				parent = cur->_parent;
			}
			else
			{
				if (cur == parent->_left)
				{
					_RotateR(grand);

					grand->_co = Red;
					parent->_co = Black;
				}
				else
				{
					_RotateL(parent);
					_RotateR(grand);

					cur->_co = Black;
					grand->_co = Red;
				}
				break;
			}
		}
		else
		{
			Node* uncle = grand->_left;
			if (uncle && uncle->_co == Red)
			{
				//这里只变色就好
				parent->_co = uncle->_co = Black;
				grand->_co = Red;

				cur = grand;
				parent = cur->_parent;
			}
			else
			{
				if (cur == parent->_right)
				{
					_RotateL(grand);

					grand->_co = Red;
					parent->_co = Black;
				}
				else
				{
					_RotateR(parent);
					_RotateL(grand);
					cur->_co = Black;
					grand->_co = Red;
				}
				break;
			}
		}
	}
	_root->_co = Black;
	//return true;
	return make_pair(iterator(newnode), true);
}

map和set内

第四步,解引用返回,因为first是个迭代器类型,对其解引用返回data值 

 

但是我们自己实现时,应该返回其data值里面的second,因为map的特性,只有second可以被修改。

故可以得出以下代码:
  注意,这是定义在map里面的,并非定义在迭代器里面

	V& operator[](const K& key)
	{
		pair<iterator, bool> result = insert(make_pair(key, V()));
		//如果存在,则插入失败
		//如果不存在,则插入数据
		//无论是否存在,都返回 second;
		return result.first->second;
	}

 四.源代码+ 测试用例

其实map和set还有很多可以值得封装的地方,这里我们给出一个比较完整的源代码:

map:
 

#pragma once
#include"RBTree.h"
using namespace MyRBTree;

namespace MyMap
{
	template<class K,class V>
	class Map
	{
	public:
		struct MapKeyOfvalue
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};
		//注意声明迭代器
		//如果想取一个类模板中的一个类型,要使用 typedname 进行声明。
		//告诉编译器这是一个类型,并不是一个静态变量
		typedef typename RBTree<K, pair<K, V>, MapKeyOfvalue>::iterator iterator;
		typedef typename RBTree<K, pair<K, V>, MapKeyOfvalue>::const_iterator const_iterator;
		pair<iterator,bool> insert(const pair<K,V> &kv)
		{
			return _map.Insert(kv);
		}

		void Inorder()
		{
			_map.Inorder();
		}

		void IsBalance()
		{
			_map.IsBalance();
		}

		V& operator[](const K& key)
		{
			pair<iterator, bool> result = insert(make_pair(key, V()));
			//如果存在,则插入失败
			//如果不存在,则插入数据
			//无论是否存在,都返回 second;
			return result.first->second;
		}

		iterator begin()
		{
			return _map.begin();
		}
	    
		iterator end()
		{
			return _map.end();
		}

		{
			return _map.begin();
		}

		iterator end()
		{
			return _map.end();
		}
	private:
		RBTree<K, pair<K,V>, MapKeyOfvalue> _map;
	};
}

set:
 

#pragma once
#include"RBTree.h"

using namespace MyRBTree;

namespace MySet
{
  template<class K>
  class Set
  {
  public:
	  struct SetKeyOfvalue
	  {
		  const K& operator()(const K& key)
		  {
			  return key;
		  }
	  };
	  //注意声明迭代器
	  //如果想取一个类模板中的一个类型,要使用 typedname 进行声明。
	  //告诉编译器这是一个类型,并不是一个静态变量
	  typedef typename RBTree<K, K, SetKeyOfvalue>::const_iterator iterator;
	  typedef typename RBTree<K, K, SetKeyOfvalue>::const_iterator const_iterator;
	  pair<iterator,bool> insert(const K& k)
	  {
		  return _set.Insert(k);
	  }
	  void Inorder()
	  {
		  _set.Inorder();
	  }
	  void IsBalance()
	  {
		  _set.IsBalance();
	  }
  private:
	  RBTree<K, K, SetKeyOfvalue> _set;
  };
} 

红黑树:

#pragma once
#include<iostream>
using namespace std;

namespace MyRBTree
{
	enum Color
	{
		Red,
		Black
	};

	//直接实现kv模型的红黑树
	template<class T>
	struct RBTreeNode
	{
		RBTreeNode(const T& data)
			:_co(Red)
			, _left(nullptr)
			, _right(nullptr)
			, _parent(nullptr)
			, _data(data)
		{}
		Color _co;
		RBTreeNode<T>* _left;
		RBTreeNode<T>* _right;
		RBTreeNode<T>* _parent;
		T _data;
	};
	//	   节点数据	 引用/const引用  指针/const指针
	template <class T, class Ref, class Ptr>
	struct _RBTreeIterator
	{
		typedef RBTreeNode<T> Node;
		typedef _RBTreeIterator<T, Ref, Ptr> self;
	
		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* left = _node->_right;
				//则寻找右子树的最左节点
				while (left->_left)
				{
					left = left->_left;
				}
				_node = left;
			}
			//如果右子树不存在
			else
			{
				//找孩子不是父亲右节点的节点
				Node* parent = _node->_parent;
				Node* cur = _node; 
				//一定要先判断parent是否存在,以此来避免越界
				while (parent&&cur == parent->_right)
				{
					cur = cur->_parent;
					parent = parent->_parent;
				}
				_node = parent;
			}
			return *this;
		}

		//迭代器后置++
		self operator++(int)
		{
			self it(_node);
			++(*this);
			return it;
		}

		self& operator--()
		{
			//如果左子树存在
			if (_node->left)
			{
				//找左子树的最右节点
				Node* right = _node->_left;
				while (right->_right)
				{
					right = right->_right;
				}
				_node = right;
			}
			//如果左子树不存在
			else
			{
				//找孩子不是父亲左节点的节点
				Node* parent = _node->parent;
				Node* cur = _node;
				while (parent&&parent->_left == cur)
				{
					cur = cur->_parent;
					parent = parent->_parent;
				}
				_node = parent;
			}
			return *this;
		}
		//后置--
		self operator--(int)
		{
			self it(_node);
			--(*this);
			return it;
		}

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

		Node* _node;
	};

	template<class K, class T, class KeyOfValue>
	class RBTree
	{
	public:
		typedef RBTreeNode<T> Node;

		typedef _RBTreeIterator<T, T&, T*> iterator;
		typedef _RBTreeIterator<T, const T&, const T*> const_iterator;


		iterator begin()
		{
			Node* left = _root;
			while (left && left->_left)
			{
				left = left->_left;
			}
			return iterator(left);
		}

		iterator end()
		{
			return iterator(nullptr);
		}


		const_iterator cbegin() const
		{
			Node* left = _root;
			while (left && left->_left)
			{
				left = left->_left;
			}
			return const_iterator(left);
		}

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

		//bool Insert(const T& data)
		//pair<iterator,bool> Insert(const T& data)	
		pair<Node*, bool> Insert(const T& data) 
		//因为set中的iterator是 const迭代器,不可以转化为iterator类型,变成node在返回时可以构造出iterator和const迭代器
		{
			if (!_root)
			{
				_root = new Node(data);
				_root->_co = Black;
				//如果成功,返回成功节点的迭代器和true
				return make_pair(_root, true);
			}

			Node* cur = _root;
			Node* parent = nullptr;

			while (cur)
			{
				
				//if (cur->_data.first < data.first)
				if (kot(cur->_data)< kot(data))
				{
					parent = cur;
					cur = cur->_right;
				}
				//else if (cur->_data.first > data.first)
				else if (kot(cur->_data) > kot(data))
				{
					parent = cur;
					cur = cur->_left;
				}
				else
				{
					//return false;
					return make_pair(cur, false);
				}
			}

			cur = new Node(data);
			cur->_co = Red;
			//注意这里cur可能因为旋转,改变了原来的位置,所以需要提前记录一下
			Node* newnode = cur;

			//if (parent->_data.first < cur->_data.first)
			if (kot(parent->_data) < kot(cur->_data))
			{
				parent->_right = cur;
			}
			else
			{
				parent->_left = cur;
			}
			cur->_parent = parent;

			//开始判断是否需要变色
			while (parent && parent->_co == Red)
			{
				Node* grand = parent->_parent;
				if (parent == grand->_left)
				{
					Node* uncle = grand->_right;
					if (uncle && uncle->_co == Red)
					{
						//这里只变色就好
						parent->_co = uncle->_co = Black;
						grand->_co = Red;

						cur = grand;
						parent = cur->_parent;
					}
					else
					{
						if (cur == parent->_left)
						{
							_RotateR(grand);

							grand->_co = Red;
							parent->_co = Black;
						}
						else
						{
							_RotateL(parent);
							_RotateR(grand);

							cur->_co = Black;
							grand->_co = Red;
						}
						break;
					}
				}
				else
				{
					Node* uncle = grand->_left;
					if (uncle && uncle->_co == Red)
					{
						//这里只变色就好
						parent->_co = uncle->_co = Black;
						grand->_co = Red;

						cur = grand;
						parent = cur->_parent;
					}
					else
					{
						if (cur == parent->_right)
						{
							_RotateL(grand);

							grand->_co = Red;
							parent->_co = Black;
						}
						else
						{
							_RotateR(parent);
							_RotateL(grand);
							cur->_co = Black;
							grand->_co = Red;
						}
						break;
					}
				}
			}
			_root->_co = Black;
			//return true;
			return make_pair(newnode, true);
		}

		void Inorder()
		{
			_Inorder(_root);
		}

		bool IsBalance()
		{
			return _IsBalance();
		}

		Node* Find(const K& key)
		{
			//KeyOfValue kot;
			Node* cur = _root;
			while (cur)
			{
				//if (cur->_data.first < key)
				if (kot(cur->_data) < key)
				{
					cur = cur->_right;
				}
				//else if (cur->_data.first > key)
				else if (kot(cur->_data) > key)
				{
					cur = cur->_left;
				}
				else
				{
					return cur;
				}
			}
			return nullptr;
		}
		//求最左节点
		Node* LeftMost()
		{
			Node* cur = _root;
			while (cur && cur->_left)
			{
				cur = cur->_left;
			}

			return cur;
		}
		//求最右节点
		Node* RightMost()
		{
			Node* cur = _root;
			while (cur && cur->_right)
			{
				cur = cur->_right;
			}
			return cur;
		}

	private:

		bool _IsBalance()
		{
			if (!_root) return true;
			if (_root->_co == Red)
			{
				cout << "根节点为红色" << endl;
				return false;
			}

			int BlackSum = 0;
			Node* cur = _root;
			while (cur)
			{
				if (cur->_co == Black) BlackSum++;
				cur = cur->_left;
			}
			return _Check(_root, 0, BlackSum);
		}

		bool _Check(Node* root, int Blacknum, int BlackSum)
		{
			if (!root)
			{
				if (Blacknum != BlackSum)
				{
					cout << "某条路径黑色节点的数量不相等" << endl;
					return false;
				}
				return true;
			}
			if (root->_co == Black)
			{
				++Blacknum;
			}
			if (root->_co == Red && root->_parent && root->_parent->_co == Red)
			{
				cout << "存在连续的红色节点" << endl;
				return false;
			}
			return _Check(root->_left, Blacknum, BlackSum) && _Check(root->_right, Blacknum, BlackSum);
		}

		//直接创建一个全局变量,递归遍历的话消耗太高
		//注意把前面我们更改的删除
		//KeyOfValue kot;
		void _Inorder(Node* root)
		{
			if (!root)
			{
				return;
			}
			//这里我们也要更改一下
			_Inorder(root->_left);
			//cout << root->_data.first << ":" << endl;
			cout <<kot(root->_data) <<" ";
			_Inorder(root->_right);
		}

		void _RotateR(Node* parent)
		{
			Node* SubL = parent->_left;
			Node* SubLR = SubL->_right;

			parent->_left = SubLR;
			if (SubLR) SubLR->_parent = parent;

			Node* pparent = parent->_parent;

			SubL->_right = parent;
			parent->_parent = SubL;

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

		void _RotateL(Node* parent)
		{
			Node* subR = parent->_right;
			Node* subRL = subR->_left;

			parent->_right = subRL;
			subR->_left = parent; //将subR的左指针指向parent

			Node* pparent = parent->_parent;

			parent->_parent = subR;//将parent的父指针指向subR

			if (subRL)
				subRL->_parent = parent;

			if (_root == parent) //判断parent是否是头节点
			{
				_root = subR;
				subR->_parent = nullptr;
			}
			else
			{
				if (pparent->_left == parent)
				{
					pparent->_left = subR;
				}
				else
				{
					pparent->_right = subR;
				}
				subR->_parent = pparent;
			}
		}

		KeyOfValue kot;
		Node* _root = nullptr;
	};
}

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

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

相关文章

2023年汉字小达人市级比赛最后一天的整体复习建议和5个提醒

今天是2023年11月29日&#xff0c;明天&#xff08;11月30日&#xff0c;星期四&#xff09;就是2023年汉字小达人市级活动&#xff08;市级比赛&#xff09;比赛的日子了。从孩子今天16点30放学&#xff0c;到明天16点开始比赛&#xff0c;除去生活时间、写学校作业&#xff0…

echarts图表滚动条带动页面窗口滚动条的问题

网上搜了很多方法不管用&#xff0c;后来发现每次滚动echarts或者左右滑动echarts下方都会报错&#xff0c;报错提示如下&#xff0c;看看你们的图表是否这样报错&#xff1a; 报错信息如下&#xff1a;Unable to preventDefault inside passive event listener invocation 原…

什么是Geo Trust OV证书

一、GeoTrust OV证书的介绍 GeoTrust OV证书是由GeoTrust公司提供的SSL证书&#xff0c;它是一种支持OpenSSL的数字证书&#xff0c;具有更高的安全性和可信度。GeoTrust是全球领先的网络安全解决方案提供商&#xff0c;为各类用户提供SSL证书和信任管理服务。GeoTrust OV证书…

在Springboot中操作Redis——五大数据类型

在Java中操作Redis Redis的Java客户端 前面我们讲解了Redis的常用命令&#xff0c;这些命令是我们操作Redis的基础&#xff0c;那么我们在java程序中应该如何操作Redis呢&#xff1f;这就需要使用Redis的Java客户端&#xff0c;就如同我们使用JDBC操作MySQL数据库一样。 Red…

pgsql 更新A表的x字段通过查询b表的z字段

查询表t_local_warning_hit_source的send_time 更新到表t_local_warning_source WITH t2 AS ( SELECT ID, send_time FROM t_local_warning_hit_source WHERE send_time > 2023-09-27 00:00:00 AND send_time < 2023-11-28 00:00:00 ) UPDATE t_local_warning_source t…

Web学习笔记

Web学习笔记 flask库前端基础超链接&#xff1a;空连接&#xff1a;图片&#xff1a;视频&#xff08;音频&#xff09;&#xff1a;嵌套使用列表表格格式化表格input表单系列 网络请求GET方式POST请求通过GET方式获取输入参数通过POST方式获取输入参数注册页面 CSS三种使用方式…

leetcode:2133. 检查是否每一行每一列都包含全部整数(python3解法)

难度&#xff1a;简单 对一个大小为 n x n 的矩阵而言&#xff0c;如果其每一行和每一列都包含从 1 到 n 的 全部 整数&#xff08;含 1 和 n&#xff09;&#xff0c;则认为该矩阵是一个 有效 矩阵。 给你一个大小为 n x n 的整数矩阵 matrix &#xff0c;请你判断矩阵是否为一…

Docker,从入门到精通

1、DockerFile 介绍 dockerfile 是啥?dockerfile 用来构建 docker 镜像的文件。 具体步骤&#xff1a; 1、编写一个 dockerfile 文件 2、docker build 构造一个镜像 3、docker run 运行镜像 4、docker push 发布镜像 DockerFile 构建过程 1、每个保留关键字都必须是大…

面试题:MySQL自增主键为什么不是连续的?

文章目录 前言一、自增值存储说明二、自增值修改机制三、 自增值修改时机四、 导致自增值不连续的原因4.1 唯一键冲突4.2 事务回滚4.3 批量写库操作 前言 提出这个问题&#xff0c;是因为在工作中发现 mysql 中的 user 表的 id 默认是自增的&#xff0c;但是数据库存储的结果却…

每日一练:约瑟夫生者死者小游戏

1. 问题描述 约瑟夫问题&#xff08;Josephus problem&#xff09;是一个经典的数学和计算机科学问题&#xff0c;源于犹太历史学家弗拉维奥约瑟夫斯&#xff08;Flavius Josephus&#xff09;的著作《犹太战记》。问题的描述如下&#xff1a;   在这个问题中&#xff0c;有n…

【古月居《ros入门21讲》学习笔记】13_服务数据的定义与使用

目录 说明&#xff1a; 1. 服务模型 2. 实现过程&#xff08;C&#xff09; 自定义服务数据 Person.srv文件内容 Person.srv文件内容说明 编译配置 在package.xml文件中添加功能包依赖 在CMakeLists.txt中添加编译选项 编译生成语言相关文件 创建服务器代码&#xf…

线下渠道应该如何控价

品牌渠道中的问题&#xff0c;大多跟价格有关&#xff0c;比如低价、窜货、假货&#xff0c;治理好这些价格问题&#xff0c;也就是在解决渠道中的低价问题&#xff0c;所以要先了解价格&#xff0c;再进行治理&#xff0c;这样的流程化操作&#xff0c;可以使品牌管控好渠道价…

基于单片机的烟雾检测报警装置(论文+源码)

1.系统设计 &#xff08;1&#xff09;利用传感器实现环境中温度、烟雾浓度的实时检测&#xff1b; &#xff08;2&#xff09;系统检测的各项数据信息通过液晶模块进行显示&#xff0c;提高设计可视化&#xff1b; &#xff08;3&#xff09;系统可以根据实际情况利用按键模…

【AIGC】关于Prompt你必须知道的特性

代码和数据:https://github.com/tonyzhaozh/few-shot-learning 一、实践验证的大模型的特性 1. 大模型的偏差 示例&#xff1a;&#xff08;文本的情感分析&#xff1a;一句话->P(积极&#xff09;或者N&#xff08;消极) Input: I hate this movie. Sentiment: Negativ…

winform 程序多语言

新建一个winform程序添加资源文件 在多语言的资源文件中设置key以及value设置button根据环境选择语言文件 namespace WindowsFormsMulLang {public partial class Form1 : Form{public Form1(){InitializeComponent();}public static ResourceManager rm new ResourceManager(…

重工业ERP包含哪些模块?能为企业带来哪些优势

化工、五金、重型机械制造等重工业行业的经营管理模式存在明显的差别化&#xff0c;企业内部的盘点、发货、接单、报价、委外、排产、派工单、工艺、品检等各业务数据的实时和准确共享有利于企业清晰掌握运作情况&#xff0c;及时制定经营策略&#xff0c;提高对市场变化的反应…

js实现鼠标拖拽

目录 css代码 html代码 js代码 完整代码 效果图&#xff1a; 需求&#xff1a; 鼠标在图片内按下时 图片可以跟随盒子动 鼠标弹起图片停下来 如果图片在box的盒子里面时鼠标弹起了 就把图片展示在box里面 并且让图片回到起始位置 css代码 .div {width: 100px;height: 10…

LeetCode Hot100 739.每日温度

题目&#xff1a; 给定一个整数数组 temperatures &#xff0c;表示每天的温度&#xff0c;返回一个数组 answer &#xff0c;其中 answer[i] 是指对于第 i 天&#xff0c;下一个更高温度出现在几天后。如果气温在这之后都不会升高&#xff0c;请在该位置用 0 来代替。 方法&…

js数组中,相同id的item数据合并

原数据&#xff1a; const list [ {id:1, key: a}, {id:1, key: b}, {id:2, key: c}, {id:2, key: d}, ]期望数据格式 const newList [ {id:1, keyList: [a,b]}, {id:2, keyList: [c,d]}, ]// 相同id的数据合并let newList_(list ).flatten().groupBy(id).map(_.spread((..…

缺省参数的声明和定义

首先&#xff0c;函数缺省参数不能同时出现在声明和定义中&#xff0c;如出现则报错&#xff1a; 声明和定义中同时出现缺省参数 ctrlb&#xff0c;编译报错&#xff0c;提示 “test"&#xff1a;重定义默认参数&#xff1a;参数1 编译报错 当函数的声明和定义中都出现…