数据结构::浅析红黑树

一、概念:

    红黑树是一棵二叉搜索树,它增加一个标志位来进行红黑颜色的表示。通过任何一条从根到叶子节点路径上的颜色来进行约束,红黑树保证最长路径是最短路径的两倍。

二、性质

   1)每个节点不是红色的就是黑色的

   2)根节点是黑色的

   3)红色节点是不连续的

   4)从根节点到每一条叶子节点的简单路径中,黑色节点的个数是相等的。

   5)每个叶子节点是黑色的

三、节点的定义:

说明:我们根据概念知道要表示红黑颜色,因此我们利用枚举来进行红黑颜色的存储

enum Color
{
	BLACK,
	RED,
};
template<class K,class V>
struct BRTreeNode
{
	K _key;
	V _value;
	Color _col;
	BRTreeNode<K,V>* _left;
	BRTreeNode<K,V>* _right;
	BRTreeNode<K,V>* _parent;
	BRTreeNode<K,V>(const K& key,const V& value)
		:_left(NULL)
		,_right(NULL)
		,_parent(NULL)
		,_key(key)
		,_value(value)
		,_col(RED)
	{}
};

四、插入函数的实现:

1、情况分析(大体上分三种情况):

(grandfa:祖父节点  parent:父节点  cur:插入的当前节点 uncle:叔叔节点)

(以下是以父节点是祖父的左孩子为例)

首先我们要明确一个情况:如果父节点是黑色的话,那么插入一个节点,就不用管了。

1)如果这棵树是一颗空树,那么插入一个节点的话,只要将这棵树的颜色编程黑色即可

2)父节点是红色,主要看叔叔节点

   A:u存在并且u为红色,只需变色,然后向上调整(将祖父节点作为当前节点)即可


   B:u不存在并且为黑色  ,要进行旋转

        a:如果插入的节点是父亲的左孩子,进行右旋


        b:如果插入的节点是父亲的右孩子,进行左右旋


        说明:有同学可能有疑问,如果u不存在的话,我将它的图画出来,我们很显而易见的发现这是和a的处理方法是一样的,只需进行左旋即可情况,所以上述的两种情况将这种情况包括进去了。


2、代码实现:

bool Insert(const K& key,const V& value)
	{
		Node* cur = _root;
		Node* parent = NULL;
		if(_root == NULL)
		{
			_root = new Node(key,value);
			_root->_col = BLACK;
			return true;
		}
		while(cur)
		{
			if(cur->_key > key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if(cur->_key < key)
			{
				parent = cur;
				cur = cur->_right;
			}
			else
			{
				return false;
			}
		}
		cur = new Node(key,value);
		if(parent->_key > key)
		{
			parent->_left = cur;
			cur->_parent = parent;
		}
		else
		{
			parent->_right = cur;
			cur->_parent = parent;
		}
		while((parent) && (parent->_col == RED))
		{
			Node* grandfa = parent->_parent;
			if(grandfa->_left == parent)
			{
				Node* uncle = grandfa->_right;
				//只需变色
				if((uncle) && (uncle->_col == RED))
				{
					parent->_col = uncle->_col = BLACK;
					grandfa->_col = RED;
					cur = grandfa;
					parent = cur->_parent;
				}
				//进行旋转
				else
				{
					if(parent->_left == cur)
					{
						RotateR(grandfa);
						//swap(parent,cur);
						parent->_col = BLACK;
						grandfa->_col = RED;
					}
					else
					{
						RotateL(parent);
						RotateR(grandfa);
						cur->_col = BLACK;
						grandfa->_col = RED;
						break;
					}
				}
			}
			else
			{
				Node* uncle = grandfa->_left;
				if((uncle) && (uncle->_col == RED))
				{
					parent->_col = uncle->_col = BLACK;
					grandfa->_col = RED;
					cur = grandfa;
					parent = cur->_parent;
				}
				else
				{
					if(parent->_right == cur)
					{
						RotateL(grandfa);
						parent->_col = BLACK;
						grandfa->_col = RED;
						//swap(cur,parent);
					}
					else
					{
						RotateR(parent);
						RotateL(grandfa);
						cur->_col = BLACK;
						grandfa->_col = RED;
						break;
					}
				}
			}
		}
		_root->_col = BLACK;
		return true;
	}

3、有关插入函数的相关函数实现

1)左旋:

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

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

		Node* ppNode = parent->_parent;
		subR->_left = parent;
		parent->_parent = subR;

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

2)右旋:

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

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

		Node* ppNode = parent->_parent;
		subL->_right= parent;
		parent->_parent = subL;

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

五、判断一颗树是否是红黑树:

思路分析:

我们由红黑树的性质出发,可以知道有以下几种情况:

1)当是空树一棵的时候,它也是红黑树

2)当它的根存在并且它的根是黑色,因此我们就让根存在,根为红色作为判断条件,因为如果是这个条件的话,我们就不用往下再进行判断了

3)红色节点的不连续性:我们可能只管想到的第一种方法就是遍历这棵树,然后记录黑色和红色节点的位置,但是这种方法时间复杂度很高,并且不易实现,因此我们想到:如果当前节点是红色的,判断它的父节点颜色是否也是红色的,如果是的话,那么就不是红黑树,否则就是。

4)判断从根节点到叶子节点的每一条简单路径中黑色节点的个数,我们肯定是采用递归了,但是有一个问题我们需要注意:树的叶子节点不都在同一水平线上的,那么计算另一分支上的上面的或者下面的黑色节点的个数的时候,就要将上次的个数减去或者增加你退回的需要拿掉的个数,所以我们在这里的变量就不使用引用了,因为上一次的变量的值的改变不能影响下一层。

要实现的两个函数:

1)判断红色节点的个数不连续

bool CheckCol(Node* root)
	{
		if(root == NULL)
			return true;
		if(root->_col == RED && root->_parent->_col == RED)
			return false;
		return CheckCol(root->_left)
			&& CheckCol(root->_right);
	}

2)判断从根节点到叶子节点的简单路径中黑色节点的个数

bool CheckBlackNum(Node* root,const size_t k,size_t num)
	{
		if(root == NULL)
			return true;
		if(root->_col == BLACK)
			num++;
		if((root->_left == NULL) && (root->_right == NULL) 
			&& (k != num))
			return false;
		return CheckBlackNum(root->_left,k,num)
		      && CheckBlackNum(root->_right,k,num);
	}
说明:我们最终要进行每一路的黑色节点的比较,那么我先计算最左或者最右一路的根节点到叶子节点的黑色节点的个数,就是这里的k

3)判断是否是红黑树的代码实现

	bool IsBRTree()
	{	
		size_t k = 0;
		size_t num = 0;
		Node* cur = _root;
		if(_root == NULL)
			return true;
		if(_root && _root->_col == RED)
			return false;
		while(cur)
		{
			if(cur->_col == BLACK)
				k++;
			cur = cur->_left;
		}
		
		return CheckCol(_root) && CheckBlackNum(_root,k,num);
	}

六、红黑树和平衡二叉树的比较

1)红黑树都是和AVL树最高效的二叉搜索树,增删查改的时间复杂度都是O(lgN)

2)红黑树不追求完全平衡,保证最长路径是最短路径的两倍,相对而言,降低了旋转的要求,所以性能优于AVL树,所以在实际应用中,红黑树的应用更多

举个例子:如果在这个图中进行插入一个节点,如果是AVL树的话,就要进行旋转,但是如果是红黑树的话,只需要进行变色。(当然只是只管的说,还会要看具体情况)


七、附源代码:

#include<iostream>
using namespace std; 
enum Color
{
	BLACK,
	RED,
};
template<class K,class V>
struct BRTreeNode
{
	K _key;
	V _value;
	Color _col;
	BRTreeNode<K,V>* _left;
	BRTreeNode<K,V>* _right;
	BRTreeNode<K,V>* _parent;
	BRTreeNode<K,V>(const K& key,const V& value)
		:_left(NULL)
		,_right(NULL)
		,_parent(NULL)
		,_key(key)
		,_value(value)
		,_col(RED)
	{}
};
template<class K,class V>
class BRTree
{
	typedef BRTreeNode<K,V> Node;
public:
	BRTree()
		:_root(NULL)
	{}
	bool Insert(const K& key,const V& value)
	{
		Node* cur = _root;
		Node* parent = NULL;
		if(_root == NULL)
		{
			_root = new Node(key,value);
			_root->_col = BLACK;
			return true;
		}
		while(cur)
		{
			if(cur->_key > key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if(cur->_key < key)
			{
				parent = cur;
				cur = cur->_right;
			}
			else
			{
				return false;
			}
		}
		cur = new Node(key,value);
		if(parent->_key > key)
		{
			parent->_left = cur;
			cur->_parent = parent;
		}
		else
		{
			parent->_right = cur;
			cur->_parent = parent;
		}
		
		while((parent) && (parent->_col == RED))
		{
			Node* grandfa = parent->_parent;
			if(grandfa->_left == parent)
			{
				Node* uncle = grandfa->_right;
				//只需变色
				if((uncle) && (uncle->_col == RED))
				{
					parent->_col = uncle->_col = BLACK;
					grandfa->_col = RED;
					cur = grandfa;
					parent = cur->_parent;
				}
				//进行旋转
				else
				{
					if(parent->_left == cur)
					{
						RotateR(grandfa);
						//swap(parent,cur);
						parent->_col = BLACK;
						grandfa->_col = RED;
					}
					else
					{
						RotateL(parent);
						RotateR(grandfa);
						cur->_col = BLACK;
						grandfa->_col = RED;
						break;
					}
				}
			}
			else
			{
				Node* uncle = grandfa->_left;
				if((uncle) && (uncle->_col == RED))
				{
					parent->_col = uncle->_col = BLACK;
					grandfa->_col = RED;
					cur = grandfa;
					parent = cur->_parent;
				}
				else
				{
					if(parent->_right == cur)
					{
						RotateL(grandfa);
						parent->_col = BLACK;
						grandfa->_col = RED;
						//swap(cur,parent);
					}
					else
					{
						RotateR(parent);
						RotateL(grandfa);
						cur->_col = BLACK;
						grandfa->_col = RED;
						break;
					}
				}
			}
		}
		_root->_col = BLACK;
		return true;
	}
	//中序打印
	void InOrder()
	{
		_InOrder(_root);
		cout<<endl;
	}
	void _InOrder(Node* root)
	{
		if(root == NULL)
			return;

		_InOrder(root->_left);
		cout<<root->_key<<" ";
		_InOrder(root->_right);
	}

	//判断是否是红黑树
	//说明:1、检查树的颜色是否是正确的
	//	2、检查树中每一条路经的黑色节点个数是否是相同的

	bool IsBRTree()
	{	
		size_t k = 0;
		size_t num = 0;
		Node* cur = _root;
		if(_root == NULL)
			return true;
		if(_root && _root->_col == RED)
			return false;
		while(cur)
		{
			if(cur->_col == BLACK)
				k++;
			cur = cur->_left;
		}
		
		return CheckCol(_root) && CheckBlackNum(_root,k,num);
	}

	//检查颜色
	bool CheckCol(Node* root)
	{
		if(root == NULL)
			return true;
		if(root->_col == RED && root->_parent->_col == RED)
			return false;
		return CheckCol(root->_left)
			&& CheckCol(root->_right);
	}

	//检查每条路经中黑节点的个数
	bool CheckBlackNum(Node* root,const size_t k,size_t num)
	{
		if(root == NULL)
			return true;
		if(root->_col == BLACK)
			num++;
		if((root->_left == NULL) && (root->_right == NULL) 
			&& (k != num))
			return false;
		return CheckBlackNum(root->_left,k,num)
		      && CheckBlackNum(root->_right,k,num);

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

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

		Node* ppNode = parent->_parent;
		subR->_left = parent;
		parent->_parent = subR;

		if(ppNode == NULL)
		{
			_root = subR;
		}
		else
		{
			if(ppNode->_left == parent)
				ppNode->_left = subR;
			else
				ppNode->_right = subR;
		}
			subR->_parent = ppNode;
	}
	//右旋
	void RotateR(Node* parent)
	{
		Node* subL= parent->_left;
		Node* subLR = subL->_right;

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

		Node* ppNode = parent->_parent;
		subL->_right= parent;
		parent->_parent = subL;

		if(ppNode == NULL)
		{
			_root = subL;
		}
		else
		{
			if(ppNode->_left == parent)
				ppNode->_left = subL;
			else
				ppNode->_right = subL;
		}
			subL->_parent = ppNode;		
	}
protected:
	Node* _root;
};
测试代码:

#include"BRTree.h"
int main()
{
	int i = 0;
	BRTree<int,int> brt1;
	//int a[8] = {5,1,2,3,7,0,9,4};
	int a[12] = {5,1,2,12,11,10,4,9,7,8,15,0};
	for(i = 0; i <sizeof(a)/sizeof(a[0]); i++)
	{
		brt1.Insert(a[i],i);
		cout<<a[i];
		cout<<"IsBRTree?"<<brt1.IsBRTree()<<endl;
	}
	brt1.InOrder();
	return 0;
}


//小结:
出现问题的地方
1)单旋之后的颜色更改问题
2)双旋之后的颜色更改问题



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值