#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#define N 10000
typedef struct RB{
/* data */
int val;
struct RB *left;
struct RB *right;
//红色是1 黑色0
int color;
struct RB *parent;
}RB;
RB* root;
int sum = 0;
int sum_node = 0;
void leftRotate(RB *p){
if(p != NULL){
//记录p的右儿子
RB *rightkid = p->right;
//1.空出右儿子的左子树
p->right = rightkid->left;
//左子树不为空,需要更新父节点
if(rightkid->left!= NULL){
rightkid->left->parent = p;
}
//空出p的父节点
rightkid->parent = p->parent;
//父节点指向右儿子
if(p->parent == NULL){
//如果p是根节点
root = rightkid;
}
else if(p == p->parent->left){
//如果p在左边
//右儿子成为父节点的左儿子
p->parent->left= rightkid;
}
else{
p->parent->right = rightkid;
}
// 3.右儿子和节点p会师,节点p成为左子树
rightkid->left = p;
p->parent = rightkid;
}
}
void rightRotate(RB *p){
if(p != NULL){
//记录p的左儿子
RB *leftkid = p->left;
//1.空出p的左儿子的右子树
p->left = leftkid->right;
//右子树不为空,则更新他的父节点
if(leftkid->right != NULL){
leftkid->right->parent = p;
}
//2.空出节点p的父节点
leftkid->parent = p->parent;
//父节点指向左儿子
if(p->parent == NULL){
root = leftkid;
}
else if(p->parent->left == p){
//如果是左儿子
p->parent->left = leftkid;
}
else{
p->parent->right = leftkid;
}
//3.顺利会师
leftkid->right = p;
p->parent = leftkid;
}
}
//插入结点x
void insert(RB *x){
RB *parent = NULL;
RB *cur = root;
while(cur!= NULL){
if(cur->val == x->val){
return;
}
else if(cur->val > x->val){
parent = cur;
cur = cur->left;
if(cur == NULL) break;
}
else{
parent = cur;
cur = cur->right;
if(cur == NULL) break;
}
}
if(parent != NULL){
if (parent->val > x->val) {
parent->left = x;
} else {
parent->right = x;
}
}
x->parent = parent;
}
RB *parentOf(RB* p){
return (p == NULL?NULL:p->parent);
}
void FixAfterInsert(RB *x){
insert(x);
//默认1是红色 黑0
x->color = 1;
///如果没有父亲 或者是父节点是黑色 则不需要调整 直接返回
if(x->parent == NULL) return;
if(x->parent->color == 0) return;
//P不为空,不是整棵树的根节点,父亲为红色,需要调整
while(x != root && x->parent->color == 1){
//父亲是祖父的左儿子
if(parentOf(x) == parentOf(parentOf(x))->left){
RB *uncle = parentOf(parentOf(x))->right;
//父亲和叔叔都是红色
if(uncle != NULL && uncle->color == 1){
//父亲和叔叔都变成黑色
parentOf(x)->color = 0;
uncle->color = 0;
//祖父变成红色,继续从祖父开始调整
parentOf(parentOf(x))->color = 1;
x = parentOf(parentOf(x));
}
//叔叔是黑色
//叔父异色,自己是左儿子,进行R操作
//叔父异色,自己是右儿子,父亲成为新的X,
//对父亲执行左旋,在执行右旋
///叔叔结点是null的话 默认为黑色结点
else{
//自己是父亲的右儿子,需要对父亲左旋
if(x == parentOf(x)->right){
x = parentOf(x);
leftRotate(x);
}
//自己是父亲的左儿子,变色后右旋
parentOf(x)->color = 0;
parentOf(parentOf(x))->color = 1;
rightRotate(parentOf(parentOf(x)));
}
}
///父亲是祖父的右儿子
else{
RB *uncle = (parentOf(parentOf(x))->left);
//父叔同为红色,只需要进行颜色调整
if(uncle != NULL && uncle->color == 1){
//叔父都变成黑色
parentOf(x)->color = 0;
uncle->color = 0;
//祖父变为红色,从祖父开始调整
parentOf(parentOf(x))->color = 1;
x = parentOf(parentOf(x));
}
//叔父异色,自己是左儿子,进行RL操作
//自己是右儿子,进行L操作
else{
if(parentOf(x)->left == x){
x = parentOf(x);
rightRotate(x);
}
//自己是父亲的右儿子,变色后左旋
parentOf(x)->color = 0;
parentOf(parentOf(x))->color = 1;
leftRotate(parentOf(parentOf(x)));
}
}
}
//最后将根节点置为黑色
root->color = 0;
}
void show(RB *p){
if(NULL == p){
sum_node++;
return;
}
//printf("%d:%d\n",p->val,p->color);
show(p->left);
show(p->right);
if(p->color == 1) sum++;
sum_node++;
}
int main(){
int num = 0;
while(num++<100){
RB *p = (RB*) malloc(sizeof (RB)*N);
srand((unsigned)time(0));
for(int i = 0;i < N;i++){
p[i].val = 1.0*rand()/RAND_MAX*N;
//printf("val is:%d\n",p[i].val);
//待插入的点的颜色为红色
p[i].color = 1;
p[i].left = NULL;
p[i].right = NULL;
p[i].parent = NULL;
}
root = &p[0];
p[0].color = 0;
for(int i = 1;i<N;i++){
FixAfterInsert(&p[i]);
}
show(root);
free(p);
}
printf("N值:%d\n红节点个数:%d,总节点(包含黑节点):%d\n",N,sum/100,sum_node/100);
printf("红节点占比:%.2f%c",(double)(sum/100)/(sum_node/100)*100,'%');
return 0;
}
在红黑树插入新节点后的调整中出了比较大的问题,主要是当新插入的节点没有叔叔节点时如何处理,上网查了很多资料才发现是要把“不存在的叔叔节点”当作黑节点来处理,这样一来红黑树的建立就得到了解决。
第二个问题是遍历问题,一开始发现N值扩大10倍,相应的红节点个数也扩大10倍,后来发现N越大,红节点个数反而不增加了。我和xyb同学讨论了一下,他认为是随机数的重复问题,导致出现相同的值的时候我的程序没有插入该节点。所以我又增加了sum_node来判断实际的节点个数,这里把空的nil节点也算进去了。随着N值增加,红节点占比保持在24.3%左右。
其实还是不太明白在产生随机数的过程中为什么会出现这么多重复的数字。或者说红节点个数的增长缓慢的并不是由于随机数的重复造成的,而是由其他因素造成的。