红黑树的实现

目录

红黑树简介

红黑树的性质

红黑树的实现

节点的实现

       迭代器的实现

树的实现

红黑树的检测


红黑树简介

红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或
Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路
径会比其他路径长出俩倍,因而是接近平衡的。

红黑树的性质

1. 每个结点不是红色就是黑色
2. 根节点是黑色的 
3. 如果一个节点是红色的,则它的两个孩子结点是黑色的 
4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点 
5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)

红黑树的实现

节点的实现

默认插入的节点是红色。

如果默认插入的节点是黑色,就会造成整个树都混乱。


	enum Color
	{
		RED,
		BLACK
	};


	// set ->key
	// map ->key/value

	template<class T>
	struct RBTreeNode
	{
		RBTreeNode<T>* _left;
		RBTreeNode<T>* _right;
		RBTreeNode<T>* _parent;

		T _data;
		Color _color;

		RBTreeNode(const T& data)
			:_left(nullptr)
			, _right(nullptr)
			, _parent(nullptr)
			, _data(data)
			, _color(RED)		//默认插入红色节点
		{}

	};

       迭代器的实现


	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->() 
		{
			return &_node->_data;
		}

		//Self& operator--()


		Self& operator++()
		{
			if (_node->_right)		//如果右树存在,那就按照中序访问右树,得到第一个节点
			{
				// 下一个就是右子树的最左节点
				Node* cur = _node->_right;
				while (cur->_left)
				{
					cur = cur->_left;
				}

				_node = cur;
			}
			else
			{
					// 左子树 根 右子树
					// 右为空,找孩子是父亲左的那个祖先
					Node* cur = _node;
					Node* parent = cur->_parent;
					while (parent && cur == parent->_right)
					{
						cur = parent;
						parent = parent->_parent;
					}

					_node = parent;
			}

			return *this;
		}

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

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

	};


其中ref与ptr用来区分const迭代器与非const迭代器

特别注意operator++操作。按照中序的顺序,当存在右节点时,去访问右节点的中序第一个节点。

不存在右节点时,去访问grand节点,知道父节点不存在,或者父节点不再是右子树

树的实现


	// set->RBTree<K, K, SetKeyOfT> _t;
	// map->RBTree<K, pair<K, T>, MapKeyOfT> _t;


	template<class K, class T, class KeyOfT>	//KeyOfT: functor to get key of T(目的是为了得到键值,而不是类型)
	class RBTree
	{
		typedef RBTreeNode<T> Node;

	public:
		typedef __RBTreeIterator<T, T&, T*> iterator;
		typedef __RBTreeIterator<T, const T&, const T*> const_iterator;		

		iterator begin()
		{
			//返回的不是根节点,而是第一个节点
			Node* cur = _root;
			while (cur && cur->_left)
			{
				cur = cur->_left;
			}
			return iterator(cur);
		}

		const_iterator begin() const
		{
			//返回的不是根节点,而是第一个节点
			Node* cur = _root;
			while (cur && cur->_left)
			{
				cur = cur->_left;
			}
			return const_iterator(cur);
		}

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

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

		pair<Node*, bool> Insert(const T& data)	//返回的是Node*与布尔值的pair,布尔值表示是否插入成功,Node*可以隐式类型构造iterator
		{
			if (_root == nullptr)
			{
				_root = new Node(data);
				_root->_color = BLACK;		//修正为黑色节点
				return make_pair(_root, true);
			}

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

			while (cur)
			{
				if (kot(date) > kot(cur->_data))		//对象()  这是一种仿函数调用的方式,这个对象可以是匿名对象,也可以是有名对象
				{
					parent = cur;
					cur = cur->_right;
				}
				else if (kot(date) < kot(cur->_data))
				{
					parent = cur;
					cur = cur->_left;
				}
				else
				{
					return make_pair(cur, false);		//已经存在相同的键值,不插入
				}

			}

			//cur为空,说明找到了插入位置

			cur = new Node(data);		//不需要调整颜色,默认为红色
			Node* newnode = cur;
			cur->_parent = parent;

			if (kot(data) > kot(parent->_data))
			{
				parent->_right = cur;
			}
			else
			{
				parent->_left = cur;
			}

			//进行调整

			while (parent && parent->_color == RED)
			{
				Node* grand = parent->_parent;
				if (parent == grand->_left)		//parent是左孩子
				{
					//     g
					//   p   u
					// c

					Node* uncle = grand->_right;

					//parent两个位置可能,cur也是,所以一共四种
					if (uncle && uncle->_color == RED)		//叔叔是红色
					{
						parent->_color = BLACK;
						uncle->_color = BLACK;
						grand->_color = RED;

						// 继续往上更新处理
						cur = grand;
						parent = cur->_parent;
					}
					else		//叔叔不存在,或者是黑色
					{
						if (cur == parent->_left)		//cur是parent的左孩子
						{
							// 单旋
							//     g
							//   p
							// c
							RotateR(grand);		//旋转
							parent->_color = BLACK;
							grand->_color = RED;
						}
						else		//cur是parent的右孩子
						{
							// 双旋 (先左旋,再右旋)
							//     g
							//   p
							//     c
							RotateL(parent);
							RotateR(grand);
							cur->_col = BLACK;
							grand->_col = RED;
						}

						break;		//旋转之后降低了高度,恢复到了插入之前的水平,可以退出循环
					}

				}

				else  // parent == grandfather->_right
				{
					//     g
					//   u   p 
					//          c
					//

					Node* uncle = grand->_left;

					if (uncle && uncle->_col == RED)
					{
						// 变色
						parent->_col = uncle->_col = BLACK;
						grand->_col = RED;

						// 继续往上处理
						cur = grand;
						parent = cur->_parent;
					}
					else	//叔叔不存在,或者是黑色
					{
						if (cur == parent->_right)
						{
							// 单旋
							//     g
							//   u	p
							//         c
							RotateL(grand);
							parent->_col = BLACK;
							grand->_col = RED;
						}
						else
						{
							// 双旋
							//     g
							//   u    p
							//      c
							RotateR(parent);
							RotateL(grand);
							cur->_col = BLACK;
							grand->_col = RED;
						}
						break;

					}

				}
			}

			_root->_col = BLACK;

			return make_pair(newnode, true);		//cur已经发生修改,所以返回的是newnode

		}

		bool IsBalance()
		{
			if (_root == nullptr)
				return true;

			if (_root->_col == RED)
				return false;

			//参考值
			int refVal = 0;
			Node* cur = _root;
			while (cur)
			{
				if (cur->_col == BLACK)
				{
					++refVal;
				}

				cur = cur->_left;
			}

			int blacknum = 0;
			return Check(_root, blacknum, refVal);
		}
		size_t Size()
		{
			return _Size(_root);
		}

		Node* Find(const K& key)
		{
			Node* cur = _root;
			while (cur)
			{
				if (cur->_kv.first < key)
				{
					cur = cur->_right;
				}
				else if (cur->_kv.first > key)
				{
					cur = cur->_left;
				}
				else
				{
					return cur;
				}
			}

			return nullptr;
		}

	pirvate:

		size_t _Size(Node* root)
		{
			if (root == nullptr)
				return 0;

			return _Size(root->_left)
				+ _Size(root->_right) + 1;
		}

		//处理好复杂的关系线
		void RotateL(Node* parent)
		{
			Node* SubR = parent->_right;
			Node* SubRL = SubR->_left;

			parent->_right = subRL;
			if (SubRL)
			{
				SubRL->_parent = parent;
			}

			subR->_left = parent;
			parent->_parent = subR;

			Node* parentParent = parent->_parent;

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

				subR->_parent = parentParent;
			}
		}



		void RotateR(Node* parent)
		{
			Node* subL = parent->_left;
			Node* subLR = subL->_right;

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

			Node* parentParent = parent->_parent;

			subL->_right = parent;
			parent->_parent = subL;

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

				subL->_parent = parentParent;
			}
		}

		// 根节点->当前节点这条路径的黑色节点的数量
		bool Check(Node* root, int blacknum, const int refVal)
		{
			if (root == nullptr)
			{
				//cout << balcknum << endl;
				if (blacknum != refVal)
				{
					cout << "存在黑色节点数量不相等的路径" << endl;
					return false;
				}

				return true;
			}

			if (root->_col == RED && root->_parent->_col == RED)
			{
				cout << "有连续的红色节点" << endl;

				return false;
			}

			if (root->_col == BLACK)
			{
				++blacknum;
			}

			return Check(root->_left, blacknum, refVal)
				&& Check(root->_right, blacknum, refVal);
		}



	private:
		Node* _root;
	};



}
检测新节点插入后,红黑树的性质是否造到破坏
因为 新节点的默认颜色是红色 ,因此:如果 其双亲节点的颜色是黑色,没有违反红黑树任何
性质 ,则不需要调整;但 当新插入节点的双亲节点颜色为红色时,就违反了性质三不能有连
在一起的红色节点 ,此时需要对红黑树分情况来讨论:
约定 :cur 为当前节点, p 为父节点, g 为祖父节点, u 为叔叔节点
(由于父节点与子节点的位置不确定,因此需要分别讨论到底是左孩子还是右孩子)
情况一 : cur 为红, p 为红, g 为黑, u 存在且为红
解决方式:将 p,u 改为黑, g 改为红,然后把 g 当成 cur ,继续向上调整。
情况二 : cur 为红, p 为红, g 为黑, u 不存在 /u 存在且为黑
p g 的左孩子, cur p 的左孩子,则进行右单旋转;相反,
p g 的右孩子, cur p 的右孩子,则进行左单旋转
p g 变色 --p 变黑, g 变红
情况三 : cur 为红, p 为红, g 为黑, u 不存在 /u 存在且为黑
p g 的左孩子, cur p 的右孩子,则针对 p 做左单旋转;相反,
p g 的右孩子, cur p 的左孩子,则针对 p 做右单旋转
则转换成了情况 2
总之,类似AVL树的旋转,看插入的位置,分为左旋、右旋、先左后右、先右后左四种情况,旋转完成之后需要变色,同时break
对于旋转算法而言,重难点是需要正确理清节点之间连接的顺序。同时需要判断特定的位置原本是不是空(防止出现空指针的解引用)。旋转完成之后,cur变成新的父节点,此时需要与grand节点判断位置,进行连接。

红黑树的检测

红黑树的检测分为两步:
1. 检测其是否满足二叉搜索树(中序遍历是否为有序序列)
2. 检测其是否满足红黑树的性质
我们算出最左路的黑色节点作为参照。遍历其他路,比较是否数据一致。
同时检测是否出现连续的红色节点。
bool Check(Node* root, int blacknum, const int refVal)
{
	if (root == nullptr)
	{
		//cout << balcknum << endl;
		if (blacknum != refVal)
		{
			cout << "存在黑色节点数量不相等的路径" << endl;
			return false;
		}

		return true;
	}

	if (root->_col == RED && root->_parent->_col == RED)
	{
		cout << "有连续的红色节点" << endl;

		return false;
	}

	if (root->_col == BLACK)
	{
		++blacknum;
	}

	return Check(root->_left, blacknum, refVal)
		&& Check(root->_right, blacknum, refVal);
}

红黑树的应用
1. C++ STL库 -- map/set、mutil_map/mutil_set
2. Java 库
3. linux内核
4. 其他一些库

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值