二叉搜索树和红黑树概述以及模板实现(2)--红黑树

39 篇文章 0 订阅

其实很多人会意识到,如果使用二叉搜索树,在数据不好的情况下,最后查找的效果会变成线性。例如数据 1 2 3 4 5 6 7,根为1,其他数据依次插入在右侧,最后变成一个线性结构,性能就会变差。如何改善二叉搜索树,使得树的高度接近logN,这种特殊的二叉搜索树就可以用红黑树来实现。

红黑树满足的五个条件

1. 每个节点或是红色,或者是黑色

2. 根是黑色的

3. 每个叶节点(NIL)是黑色的

4. 如果一个节点是红色的,则它的两个儿子都是黑色的

5. 对于每个节点,从该节点到其子孙节点的所有路径上包含相同数目的黑节点。


4 5 性质保证了 这个特殊的二叉搜索树局部上是趋近平衡的,算法导论中证明了一棵具有n个内点的红黑树,高度至多是2lg(n+1),图是一个rb树的例子



树节点的定义为:

typedef enum{RED,BLACK} Color;

	template<typename T>
	class RBTreeNode
	{
	public:
		RBTreeNode()
		{
			pLeft = pRight = pParent = NULL;
		}
		RBTreeNode(T _data):data(_data)
		{
			pLeft = pRight = pParent = NULL;
		}

	public:

		T data;
		RBTreeNode<T> *pLeft;
		RBTreeNode<T> *pRight;
		RBTreeNode<T> *pParent;
		Color color;

	};
除了多一个数据颜色以外,其他的定义和二叉搜索树是一样的。

操作:

正因为红黑树要求满足这五个性质,所以在构造插入节点的,和删除节点的过程中,都要动态局部修改树,使得性质得到继续的满足。

在插入的过程中,经常会遇到用左旋和右旋操作来改变树的结构,同时维持二叉搜索树的性质。

用一个图来说明左旋和右旋,图中是左旋,右旋是左旋的一个相反的过程。


其实要处理的包括三对指针域

例如旋转的是pNode和它的右孩子

则要修改pNode->Parent 的连接关系,pNode的父子关系,还有pNode右孩子的左孩子(14为根的那个子树)

相应的代码如下:

//left rotate
	//
	template<typename T>
	void RBTree<T>::left_rotate(RBTreeNode<T> *_pNode)
	{
		RBTreeNode<T> *pRightSon = _pNode->pRight;
		if(pRightSon == &nil)
		{
			return ;
		}
		//process the parent of _pNode
		if(_pNode->pParent != &nil)
		{
			if(_pNode->pParent->pLeft == _pNode)
			{
				_pNode->pParent->pLeft = pRightSon;
				pRightSon->pParent = _pNode->pParent;
			}
			else if(_pNode->pParent->pRight == _pNode)
			{
				_pNode->pParent->pRight = pRightSon;
				pRightSon->pParent = _pNode->pParent;
			}
		}
		//in this situation, parent of _pNode is the root
		else
		{
			m_pRoot = pRightSon;
			pRightSon->pParent = &nil;
		}



		RBTreeNode<T> *pTmp = _pNode->pLeft;

		pRightSon->pLeft = _pNode;
		_pNode->pParent = pRightSon;


		_pNode->pRight = pTmp;
		pTmp->pParent = _pNode;
	}
不难写出右旋代码。

在插入一个新节点的时候,和普通二叉搜索树一样,找到一个合适的位置插入。并且使得新的节点是红色,代码如下:

//insert a new node into the rb tree
	template<typename T>
	void RBTree<T>::insert(T data)
	{
		//create a new node
		RBTreeNode<T>* newNode = new RBTreeNode<T>(data);
		newNode->color = RED;
		newNode->pParent = newNode->pRight = newNode->pLeft = &nil;
	
		//if current tree is empty
		if(m_pRoot == NULL)
		{
			m_pRoot = newNode;
			m_pRoot -> color = BLACK;
			m_pRoot->pParent = &nil;
			return ;
		}
		//pNode for iterating
		//pInsert for insertions

		RBTreeNode<T> *pNode = m_pRoot;
		RBTreeNode<T> *pInsert = &nil;

		while(pNode != &nil)
		{
			if(data > pNode->data)
			{
				pInsert = pNode;
				pNode = pNode->pRight;
			}
			else if(data < pNode->data)
			{
				pInsert = pNode;
				pNode = pNode->pLeft;
			}
			else
			{
				return ;
			}
		}


		if(pInsert != &nil)
		{
			if(data > pInsert->data)
			{
				pInsert->pRight = newNode;
		
				
			}
			else
			{
				pInsert->pLeft = newNode;
	
			}
			newNode->pParent = pInsert;
			insert_fix_up(newNode);
		}
		

	}
但是这样一来,红黑树的性质就可能被破坏掉了。如果树种仅有一个节点,性质2被破坏,如果树中有多个节点,新插入的newNode的父亲如果是红色节点,这个时候性质4就被破坏了。实际中我们主要关注性质4被破坏,因为性质2 的破坏很容易修正,仅仅将根设为黑色即可,因为这样的操作不会破坏原有的任何性质。


如果newNode是红色,并且父亲是黑色的。很显然,性质不会破坏

只要父亲是红色的,则性质就不对。所以我们分析这个如何处理。

首先看newNode的叔叔节点,假如叔叔是红色的,这样的话,我们只要同时变化newNode的父亲和叔叔的颜色为黑色,同时设置爷爷为红色,则所有性质都可以保持。

但是如果叔叔是黑色的,则不能仅仅这样设置了,以为无论如何简单的改变颜色都会使得性质破坏掉。

所以这个时候正确的操作是先旋转在旋转。

代码如下:

template <typename T>
	void RBTree<T>::insert_fix_up(RBTreeNode<T> *_pNode)
	{
		RBTreeNode<T>* y;

		while(_pNode->pParent->color == RED)
		{
			if(_pNode->pParent == _pNode->pParent->pParent->pLeft)
			{
				y = _pNode->pParent;
				if(y->pParent->pRight->color == RED)
				{
					_pNode->pParent->pParent->pRight->color = BLACK;
					_pNode->pParent->color = BLACK;
					_pNode->pParent->pParent->color = RED;
					_pNode = _pNode->pParent->pParent;
				}
				else
				{
					if(_pNode->pParent->pRight == _pNode)
					{
						left_rotate(_pNode->pParent);
						y = _pNode;
						_pNode = _pNode->pParent;
					}

					y->color = BLACK;
					y->pParent->color = RED;
					right_rotate(y->pParent);
					_pNode = y;
					
				}
			
			}
			else if(_pNode->pParent == _pNode->pParent->pParent->pRight)
			{
				y = _pNode->pParent->pParent->pLeft;
				if(y ->color == RED)
				{
					_pNode->pParent->color = BLACK;
					_pNode->pParent->pParent->color = RED;

					y->color = BLACK;
					_pNode = _pNode->pParent->pParent;

				}
				else
				{
					y = _pNode->pParent;
					if(_pNode->pParent->pLeft == _pNode)
					{
						y = _pNode;
						_pNode = _pNode->pParent;
						right_rotate(_pNode);
					}
					y->color = BLACK;
					y->pParent->color = RED;
					left_rotate(y->pParent);
					_pNode = y;
				}
				
			}

		}
		m_pRoot->color = BLACK;

	}

完整模板代码:



#include <iostream>
#include <stack>

namespace DataStructure
{
	typedef enum{RED,BLACK} Color;

	template<typename T>
	class RBTreeNode
	{
	public:
		RBTreeNode()
		{
			pLeft = pRight = pParent = NULL;
		}
		RBTreeNode(T _data):data(_data)
		{
			pLeft = pRight = pParent = NULL;
		}

	public:

		T data;
		RBTreeNode<T> *pLeft;
		RBTreeNode<T> *pRight;
		RBTreeNode<T> *pParent;
		Color color;

	};

	/**
	*To be a r&b tree, several characteristics must be satisfied:
	* 1. for each node, it must be black or red
	* 2. root must be black
	* 3. leaf node must be black
	* 4. if one node is red, then two sons must be black
	* 5. for each node, from which to each leaf, you will get the same number of black nodes, 
	*/
	template<typename T>
	class RBTree
	{
	public:
		typedef T dateType;
		typedef RBTreeNode<T> nodeType;


	public:

		RBTree()
		{
			m_pRoot = NULL;
			nil.pParent = nil.pRight = nil.pLeft = NULL;
			nil.color = BLACK;
		}
		//insert a new node into the rb tree
		void insert(T data);

		//remove a node from the rb tree
		void remove(RBTreeNode<T> *_removeNode);

		//left rotate
		void left_rotate(RBTreeNode<T> *_pNode);

		//right rotate
		void right_rotate(RBTreeNode<T>* _pNode);

		//find the tree node
		RBTreeNode<T>* find(T _data)const;

		//print all elements
		void print_elements()const;

		//insert fix up
		void insert_fix_up(RBTreeNode<T> *_pNode);
		

	private:
		RBTreeNode<T> *m_pRoot;/*! the root of the R&B tree*/
		RBTreeNode<T> nil;
	
	};

	template<typename T>
	void RBTree<T>::print_elements()const
	{
		if(m_pRoot == NULL)
		{
			return ;
		}

		std::stack<RBTreeNode<T>*> nStack;
		
		nStack.push(m_pRoot);
		RBTreeNode<T> *tmp = m_pRoot;
		std::cout<<"All Elements"<<std::endl;

		while(!nStack.empty())
		{
			while(tmp != &nil)
			{
				tmp = tmp->pLeft;
				nStack.push(tmp);
			}

			nStack.pop();
			if(!nStack.empty())
			{
				tmp = nStack.top();
				nStack.pop();

				std::cout<<tmp->data<<std::endl;
				tmp = tmp->pRight;
				nStack.push(tmp);
			}
		}
	
	}
	//find the tree node
	template<typename T>
	RBTreeNode<T>* RBTree<T>::find(T _data)const
	{
		RBTreeNode<T>* pElement = m_pRoot;
		while(pElement != &nil && pElement->data != _data)
		{
			if(_data > pElement->data)
			{
				pElement = pElement->pRight;
			}
			else
			{
				pElement = pElement->pLeft;
			}
		}
		if(pElement != &nil)
		{
			return pElement;
		}
		return NULL;
	}

	//insert a new node into the rb tree
	template<typename T>
	void RBTree<T>::insert(T data)
	{
		//create a new node
		RBTreeNode<T>* newNode = new RBTreeNode<T>(data);
		newNode->color = RED;
		newNode->pParent = newNode->pRight = newNode->pLeft = &nil;
	
		//if current tree is empty
		if(m_pRoot == NULL)
		{
			m_pRoot = newNode;
			m_pRoot -> color = BLACK;
			m_pRoot->pParent = &nil;
			return ;
		}
		//pNode for iterating
		//pInsert for insertions

		RBTreeNode<T> *pNode = m_pRoot;
		RBTreeNode<T> *pInsert = &nil;

		while(pNode != &nil)
		{
			if(data > pNode->data)
			{
				pInsert = pNode;
				pNode = pNode->pRight;
			}
			else if(data < pNode->data)
			{
				pInsert = pNode;
				pNode = pNode->pLeft;
			}
			else
			{
				return ;
			}
		}


		if(pInsert != &nil)
		{
			if(data > pInsert->data)
			{
				pInsert->pRight = newNode;
		
				
			}
			else
			{
				pInsert->pLeft = newNode;
	
			}
			newNode->pParent = pInsert;
			insert_fix_up(newNode);
		}
		

	}
	template <typename T>
	void RBTree<T>::insert_fix_up(RBTreeNode<T> *_pNode)
	{
		RBTreeNode<T>* y;

		while(_pNode->pParent->color == RED)
		{
			if(_pNode->pParent == _pNode->pParent->pParent->pLeft)
			{
				y = _pNode->pParent;
				if(y->pParent->pRight->color == RED)
				{
					_pNode->pParent->pParent->pRight->color = BLACK;
					_pNode->pParent->color = BLACK;
					_pNode->pParent->pParent->color = RED;
					_pNode = _pNode->pParent->pParent;
				}
				else
				{
					if(_pNode->pParent->pRight == _pNode)
					{
						left_rotate(_pNode->pParent);
						y = _pNode;
						_pNode = _pNode->pParent;
					}

					y->color = BLACK;
					y->pParent->color = RED;
					right_rotate(y->pParent);
					_pNode = y;
					
				}
			
			}
			else if(_pNode->pParent == _pNode->pParent->pParent->pRight)
			{
				y = _pNode->pParent->pParent->pLeft;
				if(y ->color == RED)
				{
					_pNode->pParent->color = BLACK;
					_pNode->pParent->pParent->color = RED;

					y->color = BLACK;
					_pNode = _pNode->pParent->pParent;

				}
				else
				{
					y = _pNode->pParent;
					if(_pNode->pParent->pLeft == _pNode)
					{
						y = _pNode;
						_pNode = _pNode->pParent;
						right_rotate(_pNode);
					}
					y->color = BLACK;
					y->pParent->color = RED;
					left_rotate(y->pParent);
					_pNode = y;
				}
				
			}

		}
		m_pRoot->color = BLACK;

	}
	//remove a node from the rb tree
	template<typename T>
	void RBTree<T>::remove(RBTreeNode<T> *_removeNode)
	{

	}

	//left rotate
	//
	template<typename T>
	void RBTree<T>::left_rotate(RBTreeNode<T> *_pNode)
	{
		RBTreeNode<T> *pRightSon = _pNode->pRight;
		if(pRightSon == &nil)
		{
			return ;
		}
		//process the parent of _pNode
		if(_pNode->pParent != &nil)
		{
			if(_pNode->pParent->pLeft == _pNode)
			{
				_pNode->pParent->pLeft = pRightSon;
				pRightSon->pParent = _pNode->pParent;
			}
			else if(_pNode->pParent->pRight == _pNode)
			{
				_pNode->pParent->pRight = pRightSon;
				pRightSon->pParent = _pNode->pParent;
			}
		}
		//in this situation, parent of _pNode is the root
		else
		{
			m_pRoot = pRightSon;
			pRightSon->pParent = &nil;
		}



		RBTreeNode<T> *pTmp = _pNode->pLeft;

		pRightSon->pLeft = _pNode;
		_pNode->pParent = pRightSon;


		_pNode->pRight = pTmp;
		pTmp->pParent = _pNode;
	}

	//right rotate
	template<typename T>

	void RBTree<T>::right_rotate(RBTreeNode<T>* _pNode)
	{
		RBTreeNode<T> *pLeftSon = _pNode->pLeft;
		if(pLeftSon == &nil)
		{
			return ;
		}

		if(_pNode->pParent != &nil)
		{
			if(_pNode->pParent->pLeft == _pNode)
			{
				_pNode->pParent->pLeft = pLeftSon;
				pLeftSon->pParent = _pNode->pParent;

			}
			else
			{
				_pNode->pParent->pRight = pLeftSon;
				pLeftSon->pParent = _pNode->pParent;
			}
		}
		else
		{
			m_pRoot = pLeftSon;
			pLeftSon->pParent = &nil;
		}

		//process the parental relation between _pNode and pLeftSon
		RBTreeNode<T> *pTmp = pLeftSon->pRight;

		pLeftSon->pRight = _pNode;
		_pNode->pParent = pLeftSon;

		_pNode->pLeft = pTmp;
		pTmp->pParent = _pNode;
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值