·RB-tree(红黑树)的特点:一头一尾黑,黑同红不连
0.每个节点不是黑色就是红色;
1.根结点是黑的,NULL是当做黑结点。(一头一尾黑)
2.任一结点至NULL(树尾端)的任何路径,所含的黑结点的个数是相同的;
3.如果一个结点是红的的,那么他的俩个子结点都是黑的;
一、插入结点
会有四中情况的不平衡:新插入的结点为X,父节点P,祖父节点为G,伯父节点为S,曾祖父节点为GG
(一)这是父节点在左边的的结点的情况
(1)S为黑,且X为外侧插入;/
(2)S为黑,且X为内测插入 <
(3)S为红,且X为外侧插入 /
(4)S为红,且X为内测插入<
(二)这是父节点在右边的的结点的情况
(1)S为黑,且X为外侧插入;\
(2)S为黑,且X为内测插入 >
(3)S为红,且X为外侧插入 \
(4)S为红,且X为内测插入>
这来俩种情况只有略微的差别见代码:
在写插入函数的时候,我总结了如下几点:1.因为给函数传过去的是子节点的父节点,所以就先定义一个结点让他为子节点就如RBNode *s = p->LeftChild(p是父节点);
2.看他是左旋还是右旋,如果是左旋,就先安排子节点的左子树,看他和谁连,我们应该知道旋转之后,父节点会降下来,所以父节点 的右结点连接s的左结点,p->rightChild=s->leftChild;然后紧接着让s->leftChild->parent = p不过还是s->leftChild不为空的情况下才有意义;
3.然后安排s结点,先考虑s结点被谁连,我们知道s结点在旋转之后会顶替原来的父节点,所以他应该被原来的父节点的连接,即if(p == p->parent->leftChild)p->parent->leftChild = s;
else p->parent->rightChild =s;
紧接着让s->parent = p->parent;
4.然后安排s去连谁?我们知道旋转之后原来的p(父节点)变成子节点s变成了父节点,所以s去连p;s->leftChild =p;
紧接着让p->parent = s;
按着这个思路就好了,写右旋转的时候依然根据这个思路;
#ifndef RB_TREE_H
#define RB_TREE_H
typedef int Type;
typedef enum{RED=0, BLACK}COLOR;
class RBNode
{
public:
COLOR color;
Type data;
RBNode *parent;
RBNode *leftChild;
RBNode *rightChild;
};
//typedef struct RBTree
class RBTree
{
public:
RBNode *root;
RBNode *NIL;
};
RBNode* _Buynode(Type v)
{
RBNode *s = (RBNode*)malloc(sizeof(RBNode));
assert(s != NULL);
memset(s, 0, sizeof(RBNode));
s->data = v;
s->color = RED;
return s;
}
void InitRBTree(RBTree &t)
{
t.NIL = _Buynode(0);
t.root = t.NIL;
t.NIL->color = BLACK;
}
void LeftRotate(RBTree &t, RBNode *p)
{
RBNode *s = p->rightChild;
p->rightChild = s->leftChild;
if(s->leftChild != t.NIL)
s->leftChild->parent = p;
s->parent = p->parent;
if(p->parent == t.NIL)
t.root = s;
else if(p == p->parent->leftChild)
p->parent->leftChild = s;
else
p->parent->rightChild = s;
s->leftChild = p;
p->parent = s;
}
void RightRotate(RBTree &t, RBNode *p)
{
RBNode *s = p->leftChild;
p->leftChild = s->rightChild;
if(s->rightChild != t.NIL)
s->rightChild->parent = p;
s->parent = p->parent;
if(p->parent == t.NIL)
t.root = s;
else if(p == p->parent->leftChild)
p->parent->leftChild = s;
else
p->parent->rightChild = s;
s->rightChild = p;
p->parent = s;
}
void Insert_Fixup(RBTree &t, RBNode *z)
{
RBNode *y;
while(z->parent->color == RED)
{
if(z->parent == z->parent->parent->leftChild)
{
y = z->parent->parent->rightChild;//叔父结点;
if(y->color == RED)
{
//状况3,S为红且X为外侧插入这是一种不通过左旋转或者是右旋转直接通过颜色来调节平衡
z->parent->color = BLACK;
y->color = BLACK;
z->parent->parent->color = RED;
z = z->parent->parent;//至于状况4可以就是将z变成他的祖父节点看他的颜色是否是红色,继续循环回来调整
continue;
}
else if(z == z->parent->rightChild)
{
//状况2:叔父结点S为黑色,X为内测插入;
//经过左旋转之后将其转化为叔父结点S为黑色,X成为外侧插入;
z = z->parent;
LeftRotate(t, z);
}
//状况1:叔父结点S是黑色X为外侧插入的情况时先更改P,G的颜色,在右旋;
z->parent->color = BLACK;
z->parent->parent->color = RED;
RightRotate(t, z->parent->parent);
}
else 这是上面说的第二种情况(二)
{
//不同之处也是将原来的左旋换成了右旋,右旋换成了左旋;
y = z->parent->parent->leftChild;
if(y->color == RED)
{
z->parent->color = BLACK;
y->color = BLACK;
z->parent->parent->color = RED;
z = z->parent->parent;
continue;
}
else if(z == z->parent->leftChild)
{
z = z->parent;
RightRotate(t, z);
}
z->parent->color = BLACK;
z->parent->parent->color = RED;
LeftRotate(t, z->parent->parent);
}
}
t.root->color = BLACK;
}
bool Insert(RBTree &t, Type x)
{
RBNode *pr = t.NIL;
RBNode *s = t.root;
while(s != t.NIL)
{
if(x == s->data)
return false;
pr = s;
if(x < s->data)
s = s->leftChild;
else
s = s->rightChild;
}
RBNode *q = _Buynode(x);
q->parent = pr;
q->leftChild = t.NIL;
q->rightChild = t.NIL;
q->color = RED;
if(pr == t.NIL)
t.root = q;
else if(x < pr->data)
pr->leftChild = q;
else
pr->rightChild = q;
Insert_Fixup(t, q);
return true;
}
#include<iostream>
#include<assert.h>
#include"rb_tree.h"
using namespace std;
void main()
{
//int ar[] = {100, 70, 80, 50, 40, 75, 73, 72, 74};
//int ar[]={85,80,90,75};//这在验证S为红,X为外侧插入;
int ar[]={10,5,8};
int n = sizeof(ar) / sizeof(int);
RBTree rb;
InitRBTree(rb);
for(int i=0; i<n; ++i)
{
Insert(rb, ar[i]);
}
return ;
}