红黑树的插入操作详解(插入调整)

16 篇文章 0 订阅
1 篇文章 0 订阅

       c++标准库中STL的关联容器(set、multiset、map、multimap)都是用的红黑树作为底层代码,而且红黑树在快速查找的结构里面用的比较多,想比于平衡二叉树,红黑树没有那么严格的限制条件(平衡二叉树要求结点的深度之差不超过1,而红黑树则没有这个限制),虽然红黑树是一种弱化的AVL树,但在数据查找效率方面依然不必AVL差,而且由于AVL的平衡条件要求比较严格,所以,当插入一个数据时,极有可能就需要经过旋转操作来使其重新达到平衡,所以平衡二叉树运用的范围更加广泛,但由于其增加了颜色标识,所以操作起来也更加复杂。

一、红黑树的性质

       一般的,红黑树,满足以下性质,即只有满足以下全部性质的树,我们才称之为红黑树:

  1. 每个节点要么是红的,要么是黑的。
  2. 根节点是黑的。
  3. 每个叶节点(叶节点即指树尾端NIL指针或NULL节点)是黑的。
  4. 如果一个节点是红的,那么它的两个儿子都是黑的。
  5. 对于任一节点而言,其到叶节点树尾端NIL指针的每一条路径都包含相同数目的黑节点。

  如下图,就是一个典型的红黑树:


二、红黑树和AVL树的比较

       红黑树:红黑树并不追求“完全平衡”——它只要求部分地达到平衡要求,降低了对旋转的要求,从而提高了性能。红黑树能够以O(log2n)的时间复杂度进行搜索、插入和删除操作,而且任何不平衡都会在三次旋转之内解决,红黑树的算法时间和AVL相同,但统计性能要比AVL好。

       AVL树:平衡二叉树或为空树,或为如下性质的二叉排序树:
                   (1)左右子树深度之差的绝对值不超过1;
                   (2)左右子树仍然为平衡二叉树.

        因为平衡二叉树的所有节点有平衡限制,所以,查找速度比较快,不会因为深度过深而影响搜索时间。


三、红黑树的插入、平衡及旋转代码展示

        由于删除操作的思路还没理清,所以暂时先没写,等到时理解后在重新编写完善该文章。把自己的理解结合STL源码,编写代码代码如下:


#include <iostream>
using namespace std;
enum {BLACK=0,RED};   //表示红黑树的两种颜色

//红黑树的结点数据结构
struct rb_tree_node 
{
public:
	char data;
	rb_tree_node* parents;
	rb_tree_node* left;
	rb_tree_node* right;
	int color;
};

//操作红黑树的类
class My_rb_tree
{
public:
	typedef rb_tree_node*  rb_pointer; 

private:
	rb_pointer header;   //一个头,与根节点互为父结点
public:
	My_rb_tree()   //构造函数
	{
		_init();
	}
	
	//插入结点,insert_equal
	rb_pointer insert_equal(const char &data)
	{
		rb_pointer x=_root();  //得到根节点
		rb_pointer tem=header;
		while(x!=nullptr)
		{
			tem=x;
			x=(x->data)>data?x->left:x->right;
		}
		_insert(tem,data);
		return x;
	}

	void print()
	{
		cout<<_left_most()->data<<endl;
		cout<<_right_most()->data<<endl;
	}
private:
	void _init()    //初始化
	{
		header=_get_node(); //为头结点分配一个内存
		header->data='#';
		header->color=RED;    //color为空,与root区别
		header->left=header;
		header->right=header;    //左右孩子为空
		header->parents=nullptr;  //指向根节点,开始为空
	}

	rb_pointer _get_node()
	{
		return (rb_pointer)malloc(sizeof(rb_tree_node));   //为结点配置一个内存
	}
	
	//获得根节点
	rb_pointer& _root() const
	{
		return (rb_pointer&)header->parents;
	}
	rb_pointer& _left_most()const
	{
		return (rb_pointer&)header->left;
	}
	rb_pointer& _right_most()const
	{
		return (rb_pointer&)header->right;
	}

	rb_pointer _min(rb_pointer x)
	{
		while(x->left!=nullptr)
			x=x->left;
		return x;
	}
	rb_pointer _max(rb_pointer x)
	{
		while(x->right!=nullptr)
			x=x->right;
		return x;
	}

	rb_pointer _insert(rb_pointer parents,const char& data)
	{
		rb_pointer  new_node=nullptr;
		if(parents==header)    //当前结点是header结点,则,插入的是root结点
		{
			new_node=_get_node();
			//设置数据
			new_node->data=data;
			new_node->left=nullptr;
			new_node->right=nullptr;
			new_node->parents=parents;
			new_node->color=RED;

			header->parents=new_node;
			_left_most()=new_node;
			_right_most()=new_node;
			_root()=new_node;		
		}
		else  //当前结点不是header结点
		{
			if(data<parents->data)  //值小于当前结点 
			{
				new_node=_get_node();
				//设置数据
				new_node->data=data;
				new_node->left=nullptr;
				new_node->right=nullptr;
				new_node->parents=parents;
				new_node->color=RED;

				parents->left=new_node;

				if(header->left==parents)  //当前结点是最小值
					_left_most()=new_node;   //修改当前最小的值,_left_most()返回的必须是引用,不然不能修改该值,以下同理
			}
			else        //值大于当前结点
			{
				new_node=_get_node();
				//设置数据
				new_node->data=data;
				new_node->left=nullptr;
				new_node->right=nullptr;
				new_node->parents=parents;
				new_node->color=RED;

				parents->right=new_node;

				if(header->right==parents)
					_right_most()=new_node;   //修改当前最大的值
			}

		}
		//调整红黑特性
		 _rb_tree_rebalance(new_node,_root());

		return new_node;
	}

	void  _rb_tree_rebalance(rb_pointer new_node,rb_pointer& root)
	{
		while(new_node!=root&&new_node->parents->color==RED)   //当前节点不为根节点,且父结点为红(只有父结点为红才需要调整),父结点是祖父结点的左孩子
		{
			
			if(new_node->parents==new_node->parents->parents->left)  //父结点是祖父结点的左孩子,右孩子类似
			{
				rb_pointer uncle=new_node->parents->parents->right;
				if(uncle!=nullptr&&uncle->color==RED)   //case1:父结点是红色的,伯父结点存在且为红
				{
					uncle->parents->color=RED;
					new_node->parents->color=BLACK;
					uncle->color=BLACK;
					new_node=uncle->parents;
				}
				else                                   //case2:父结点是红色的,而伯父结点不存在或伯父结点为黑
				{
					if(new_node==new_node->parents->right) //如果插入的点是父结点的右孩子时,要进行双旋转,先进行左旋转
					{
						new_node=new_node->parents;
						_rotate_left(new_node,root); ///以插入点的父结点为基准,左旋转
					}
					new_node->parents->color=BLACK;   //改变基准点的父结点的颜色
					new_node->parents->parents->color=RED;
					_rotate_right(new_node->parents->parents,root);  //如果插入的是父结点的左孩子,则只要进行右旋转就行了
				}
			}
			else     父结点是祖父结点的右孩子
			{
				rb_pointer uncle=new_node->parents->parents->left;
				if(uncle!=nullptr&&uncle->color==RED)   //case3:父结点是红色的,伯父结点存在且为红
				{
					uncle->parents->color=RED;
					new_node->parents->color=BLACK;
					uncle->color=BLACK;

					new_node=uncle->parents;
				}
				else                                   //case4:父结点是红色的,而伯父结点不存在或伯父结点为黑
				{
					if(new_node==new_node->parents->left) //如果插入的点是父结点的右孩子时,要进行双旋转,先进行左旋转
					{
						new_node=new_node->parents;
						_rotate_right(new_node,root); ///以插入点的父结点为基准,左旋转
					}
					new_node->parents->color=BLACK;   //改变基准点的父结点的颜色
					new_node->parents->parents->color=RED;
					_rotate_left(new_node->parents->parents,root);  //如果插入的是父结点的左孩子,则只要进行右旋转就行了
				}
			}
		}

		root->color=BLACK;  //设置根节点为黑,最终会到达根节点,然后直接改根节点的颜色就可以了
	}

	void _rotate_left(rb_pointer x,rb_pointer& root)
	{
		rb_pointer y=x->right;
		x->right=y->left;
		if(y->left!=nullptr)// 当前的x结点有左孩子,则应设置该左孩子的父结点
			y->left->parents=x;
		y->parents=x->parents;
		if(x==_root())
			_root()=y;
		else if(x==x->parents->left)   //x是父结点的左孩子
		{
			x->parents->left=y;
		}
		else if(x==x->parents->right)   //x是父结点的右孩子
		{
			x->parents->right=y;
		}
		x->parents=y;
		y->left=x;
	}

	void _rotate_right(rb_pointer x,rb_pointer& root)
	{
		rb_pointer y=x->left;
		x->left=y->right;
		if(y->right!=nullptr)
		{
			y->right->parents=x;
		}
		y->parents=x->parents;
		if(x==_root())
		{
			_root()=y;
		}
		else if(x==x->parents->left)
			x->parents->left=y;
		else if(x==x->parents->left)
			x->parents->right=y;
		x->parents=y;
		y->right=x;
	}
};

int _tmain(int argc, _TCHAR* argv[])
{
	My_rb_tree rb;
	rb.insert_equal('1');  //这里只是字符,可以改为int型更好
	rb.insert_equal('5');
	rb.insert_equal('6');
	rb.insert_equal('2');
	rb.print();   //打印最小值和最大值
	return 0;
}

输出结果如下:

测试成功!


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值