红黑树

看红黑树之前先看AVL树有很多相似的地方,我也写了一篇AVL树的总结,我就没重复总结了。

 

我们先看红黑树特性

1.根节点是黑色

2.每个节点只能是黑色或者红色

3.如果一个节点是红色,它得子节点必须是黑色的。

4.每个叶子节点(NIL)是黑色的,这里可以是所有叶子节点连着最后一个(黑色的)哨兵节点(为NULL)。

5.从任意一个节点到其叶子节点的所有路径中,包含相同数目的黑色的节点。(不能从父节点回退)

看一下红黑树的长相

红黑树的高度问题

由于它的特性所影响,从根节点出发,整个红黑树的最长路径(树的高度)不会超过最短路径的2倍。看张图就知道了

看完图也可以看一下推导思路

一棵含n个结点的红黑树最大的高度不超过2log(n+1)。我简单说下思路

(1)我们先假设该定理成立  推导出     h <= 2log(n+1)    再推导得       n >  2 ^(h/2) -1             (^ 是平方的意思)

(2)上面推导出 一棵高度为h 的红黑树它至少 有 n 个节点

(3) 我们设任意节点到该叶子节点的黑结点个数为B(hx)。特性3  红色节点子节点必须是黑色的。得出一棵树的高度的  B(hx) >=h/2。原因有以下几点。

结点只能为黑或者红。红节点子节点为黑。

意味着一条路径有一个红就会有一个黑节点。

从任意一个节点到其叶子节点得所有路径中,包含相同数目的黑色节点。意味着黑色节点数目唯一性。

而树的高度。便是最长路径的节点的个数,推出 该路径的 "黑节点个数" >= h/2      也就是       B(hx) >= h/2

(4) 根据两个公式   n >=  2^(h/2)-1    和  B(hx) >=h/2     推出    n  >=  2^(B(hx)-1

(5)数学归纳法过程

红黑树高度 = 0      带入公式 n>= 2^B(hx)-1            0>= 0成立

红黑树高度为 h-1     黑节点个数 为 B(hx) (从红节点开始) 或者B(hx)-1  (从黑节点开始) 

我们要证明高度为h  至少含多少个内节点  。这时候就要取高度h-1  黑节点小的时候。

n > = (2^(B(hx)-1) -1) + (2^(B(hx)-1 )-1) +1   (这种情况是从黑节点开始的,所以+1)

n>= (2^(B(hx)-1)

 

再看一下红黑树的结构体

Color Type 是枚举类型   说明它的性质非红即黑。(不要和我说没颜色)

Nil 是一个哨兵位

 

typedef int KeyType;
typedef enum {RED = 0, BLACK = 1} ColorType;
typedef struct rb_node
{
	rb_node *leftchild;
	rb_node *parent;
	rb_node *rightchild;
	ColorType color; // 
	KeyType key;
}rb_node;

typedef struct        //头节点head
{
	rb_node *head;
	rb_node *Nil;
	int cursize;
}RBTree;

 

平衡二叉树和红黑树的区别

 (1) 平衡二叉树和红黑树都是通过特性来控制树的高度

平衡二叉树控制平衡相对比较严格,左子树高度和右子树的高度差不能超过1。

而红黑树是通过特性和颜色来相对控制其平衡

(2)平衡二叉树插入新节点时候,效率会低很多,红黑树则效率快一些

 

我们分析一下几种插入情况插入时候,(新插入节点是红色,但是他的颜色在双旋之后是可以变黑的。这个颜色不能随着旋转之类的变掉颜色)

新节点一定是红色,插入进去,父节点是黑色,皆大欢喜,我直接插入,不要调整

但是如果是父节点为红色

我们画个图

第一种情况

叔叔爸爸都为红,就把爷爷染红,叔叔爸爸染黑

 

第二种情况

叔叔为爷爷的左孩子且为黑色节点,父亲节点为红色,这时候就需要左旋或者双旋

看图

第三种情况

叔叔为右节点且为黑色,父亲为红色,可能要右旋或者双旋。

 

这个是核心代码你看下

//pa为新节点的父节点
void Insert_Item(RBTree *ptree,rb_node *pa,KeyType kx)
{
	rb_node *s = Buynode(pa,RED);   //创新节点
	s->key = kx;
	s->leftchild = ptree->Nil;
	s->rightchild = ptree->Nil;
	if(kx < pa->key)    //当kx 小于pa->key
	{
		pa->leftchild = s;   //s为pa的左孩子
		if(kx < ptree->head->leftchild->key)  //头连着左孩子连着整个树最小关键词  
		{
			ptree->head->leftchild = s;
		}
	}

	else
	{
		pa->rightchild = s;
		if(kx > ptree->head->rightchild->key)      //头右孩子连着最大关键词
		{
			ptree->head->rightchild = s;
		}
	}
	//
	rb_node *_Y = NULL;          // _Y 为叔叔节点
	rb_node *_X = s;            //_X在循环里为孙子节点
	for(;_X != ptree->head->parent && _X->parent->color == RED ;)
	{
		if(_X->parent == _X->parent->parent->rightchild)
		{
			_Y = _X->parent->parent->leftchild;
			if(_Y->color == RED)  //变颜色即可
			{
				_Y->color = BLACK;
				_X->parent->color = BLACK;
				_X->parent->parent->color = RED;
				_X = _X->parent->parent;
			}
			else
			{
				if(_X->parent->leftchild == _X)   //双旋情况
				{
					_X = _X->parent; 
					RotateRight(ptree,_X);
				}        

				_X->parent->color = BLACK;     //没执行上面的if为单旋
				_X->parent->parent->color = RED;
				RotateLeft(ptree,_X->parent->parent);
			}
		}
		else
		{
			_Y = _X->parent->parent->rightchild;
			if(_Y->color = RED)
			{
				_Y->color = BLACK;
				_X->parent->color = BLACK;
				_X->parent->parent->color = RED;
				_X = _X->parent->parent;
			}
			else
			{
				if(_X->parent->rightchild == _X)
				{
					_X = _X->parent;
					RotateLeft(ptree,_X);
				}
				_X->parent->color = BLACK;
				_X->parent->parent->color = RED;
				RotateRight(ptree,_X->parent->parent);
			}
		}
	}
	ptree->head->parent->color = BLACK;    //就是上面说的根就算变红了最后循环出来涂黑就好

}

 

我们看一下红黑树的左旋

这个左旋和 平衡二叉树里提及的左旋是一个道理。我在AVL树博客里总结了,我这里简单提一下

节点颜色的调整都是在Insert_Item(BTree *ptree,rb_node *pa,KeyType kx)

这里左旋函数只关心节点的调整,不用关心节点颜色。

判断时候不要拿NULL 来判断,拿哨兵节点来判断

 

void RotateLeft(RBTree *ptree,rb_node *ptr)
{
	rb_node * newroot = ptr->rightchild;
	newroot->parent = ptr->parent; //1
	ptr->rightchild = newroot->leftchild;
	if(newroot->leftchild != ptree->Nil)
	{
		newroot->leftchild->parent = ptr;//2
	}

	newroot->leftchild = ptr;
	if(ptree->head->parent == ptr)
	{
		ptree->head->parent = newroot;
	}
	else
	{
		if(ptr->parent->rightchild == ptr)
		{
			ptr->parent->rightchild = newroot;
		}
		else
		{
			ptr->parent->leftchild = newroot;
		}
	}
	ptr->parent = newroot;
}

 

看下总代码

#include<iostream>
#include<stack>
#include<queue>
#include <assert.h>
using namespace std;
typedef int KeyType;
typedef enum {RED = 0, BLACK = 1} ColorType;

typedef struct rb_node
{
	rb_node *leftchild;
	rb_node *parent;
	rb_node *rightchild;
	ColorType color; // 
	KeyType key;
}rb_node;

typedef struct 
{
	rb_node *head;
	rb_node *Nil;
	int cursize;
}RBTree;

rb_node * Buynode(rb_node *pa = NULL,ColorType color = RED)
{
	rb_node *s = (rb_node*)malloc(sizeof(rb_node));
	if(NULL == s) exit(1);
	memset(s,0,sizeof(rb_node));
	s->parent = pa;
	s->color = color;
	s->leftchild = NULL;
	s->rightchild = NULL;
	return s;
}

void Freenode(rb_node *p)
{
	free(p);
}

void Init_BTree(RBTree *ptree)
{
	assert(ptree != NULL);
	ptree->Nil = Buynode(NULL,BLACK);
	ptree->head = Buynode(ptree->Nil,RED);
	ptree->head->leftchild = ptree->Nil;
	ptree->head->rightchild = ptree->Nil;
	ptree->cursize = 0;
}

void RotateLeft(RBTree *ptree,rb_node *ptr)
{
	rb_node * newroot = ptr->rightchild;
	newroot->parent = ptr->parent; //1
	ptr->rightchild = newroot->leftchild;
	if(newroot->leftchild != ptree->Nil)
	{
		newroot->leftchild->parent = ptr;//2
	}

	newroot->leftchild = ptr;
	if(ptree->head->parent == ptr)
	{
		ptree->head->parent = newroot;
	}
	else
	{
		if(ptr->parent->rightchild == ptr)
		{
			ptr->parent->rightchild = newroot;
		}
		else
		{
			ptr->parent->leftchild = newroot;
		}
	}
	ptr->parent = newroot;
}

void RotateRight(RBTree *ptree,rb_node *ptr)
{
	rb_node *newroot = ptr->leftchild;
	ptr->leftchild = newroot->rightchild;
	newroot->rightchild = ptr;
	if(ptree->head->parent == ptr)
	{
		ptree->head->parent = newroot;
	}
	else
	{
		if(ptr->parent->leftchild == ptr)
		{
			ptr->parent->leftchild = newroot;
		}
		else
		{
			ptr->parent->rightchild = newroot;
		}
	}
}

void Insert_Item(RBTree *ptree,rb_node *pa,KeyType kx)
{
	rb_node *s = Buynode(pa,RED);
	s->key = kx;
	s->leftchild = ptree->Nil;
	s->rightchild = ptree->Nil;
	if(kx < pa->key)
	{
		pa->leftchild = s;
		if(kx < ptree->head->leftchild->key)
		{
			ptree->head->leftchild = s;
		}
	}
	else
	{
		pa->rightchild = s;
		if(kx > ptree->head->rightchild->key)
		{
			ptree->head->rightchild = s;
		}
	}
	//
	rb_node *_Y = NULL;
	rb_node *_X = s;
	for(;_X != ptree->head->parent && _X->parent->color == RED ;)
	{
		if(_X->parent == _X->parent->parent->rightchild)
		{
			_Y = _X->parent->parent->leftchild;
			if(_Y->color == RED)
			{
				_Y->color = BLACK;
				_X->parent->color = BLACK;
				_X->parent->parent->color = RED;
				_X = _X->parent->parent;
			}
			else
			{
				if(_X->parent->leftchild == _X)
				{
					_X = _X->parent;
					RotateRight(ptree,_X);
				}
				_X->parent->color = BLACK;
				_X->parent->parent->color = RED;
				RotateLeft(ptree,_X->parent->parent);
			}
		}
		else
		{
			_Y = _X->parent->parent->rightchild;
			if(_Y->color = RED)
			{
				_Y->color = BLACK;
				_X->parent->color = BLACK;
				_X->parent->parent->color = RED;
				_X = _X->parent->parent;
			}
			else
			{
				if(_X->parent->rightchild == _X)
				{
					_X = _X->parent;
					RotateLeft(ptree,_X);
				}
				_X->parent->color = BLACK;
				_X->parent->parent->color = RED;
				RotateRight(ptree,_X->parent->parent);
			}
		}
	}
	ptree->head->parent->color = BLACK;

}

bool Insert(RBTree *ptree,KeyType kx)
{
	if(ptree == NULL) return false;       
	if(ptree->head->parent == ptree->Nil)            //此时是个空树
	{
		rb_node *s = Buynode(ptree->head,BLACK);
		s->key = kx;
		s->leftchild = ptree->Nil;
		s->rightchild = ptree->Nil;
		ptree->head->parent = s;
		ptree->head->leftchild = s;
		ptree->head->rightchild = s;
		ptree->cursize = 1;
		return true;
	}
	/
	rb_node *pa = ptree->head;                   
	rb_node *p = ptree->head->parent; // root;
	while(p != ptree->Nil && p->key != kx)
	{
		pa = p;
		p = kx < p->key ? p->leftchild:p->rightchild;
	}
	if(p != ptree->Nil && p->key == kx) return false;
	Insert_Item(ptree,pa,kx);
	ptree->cursize+=1;
	return true;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值