红黑树学习整理

定义:

1.每个节点不是红色就是黑色;

2.根节点是黑色

3.如果一个节点是红色,则他的两个孩子节点是黑色(从每个叶子到跟的所有路径不能有两个连续的红色节点)

4.对于每个节点,从该节点到其所有后代节点的简单路径上,均包含相同数目的黑色节点

5.每个叶子节点都是黑色的(此处的叶子节点是值空节点如下图解释,看似24是叶子节点,但对于红黑树来说,最后的两个空子节点才是叶子节点)

二叉树节点和二叉树的代码定义

typedef struct _rbtree_node {
    unsigned char color;
    struct _rbtree_node *left;
    struct _rbtree_node *right;
    struct _rbtree_node *parent;

    KEY_TYPE key;
    void *value;
} rbtree_node;

typedef struct _rbtree {
    rbtree_node *root;  // 红黑树的根节点
    rbtree_node *nil;  // 红黑树的 叶子节点 在创建红黑树时候 初始化一个节点, 今后 每个新增的节点如果没有子节点的话,子节点默认指向树的叶子节点
} rbtree;

二叉树的初始化创建

	int keyArray[20] = {24,25,13,35,23, 26,67,47,38,98, 20,19,17,49,12, 21,9,18,14,15};

	rbtree *T = (rbtree *)malloc(sizeof(rbtree));
	if (T == NULL) {
		printf("malloc failed\n");
		return -1;
	}
	
	T->nil = (rbtree_node*)malloc(sizeof(rbtree_node));
	T->nil->color = BLACK;  // 初始化红黑树的叶子节点
	T->root = T->nil;  // 初始化根节点

在分析红黑树的插入之前,需要先了解平衡二叉树的旋转:左旋、右旋。在红黑树的插入后,可以通过旋转来维持红黑树的平衡特性

旋转是二叉树的特性,并不是红黑树的特性,需要注意的是,旋转的前后,二叉树的有序性不能被破坏,即旋转前后二叉树的左子树小于根,小于右子树(当然你的二叉树也可以是左子树大于跟,大于右子树)

 已左旋为例:x的右子树指向y的左子树,y的左子树的父指向x,y的左子树指向x,y的父指向x的父,x的父的孩子(左或者右,需要判断)指向y,x的父指向y

右旋和左旋逻辑一样,左右旋代码实现如下:

void _left_rotate(rbtree *T, rbtree_node *x) {
    rbtree_node *y = x->right;

    x->right = y->left;
    if (y->left != T->nil) {
        y->left->parent = x;
    }

    y->left = x;
    y->parent = x->parent;

    if (x->parent == T->nil) {
        T->root = y;
    } else if (x->parent->left == x) {
        x->parent->left = y;
    } else {
        x->parent->right = y;
    }

    x->parent = y;
}

void rbtree_right_rotate(rbtree *T, rbtree_node *y) {

    rbtree_node *x = y->left;

    y->left = x->right;
    if (x->right != T->nil) {
        x->right->parent = y;
    }

    x->parent = y->parent;
    if (y->parent == T->nil) {
        T->root = x;
    } else if (y == y->parent->right) {
        y->parent->right = x;
    } else {
        y->parent->left = x;
    }

    x->right = y;
    y->parent = x;
}

然后开始分析插入

二叉树在插入的时候,被插入的节点肯定是要插入到叶子节点的子节点下 ,所以需要先查找插入位置,然后再进行插入,插入的节点默认为红,因为为红的时候,最多只会破坏红黑树的一条规则第三条,不能连续两个红节点,插入后,再进行平衡的修复

void rbtree_insert(rbtree *T, rbtree_node *z) {

	rbtree_node *y = T->nil;
	rbtree_node *x = T->root;

	while (x != T->nil) {
		y = x;
		if (z->key < x->key) {
			x = x->left;
		} else if (z->key > x->key) {
			x = x->right;
		} else { //Exist
			return ;
		}
	}

	z->parent = y;
	if (y == T->nil) {
		T->root = z;
	} else if (z->key < y->key) {
		y->left = z;
	} else {
		y->right = z;
	}

	z->left = T->nil;
	z->right = T->nil;
	z->color = RED;

	rbtree_insert_fixup(T, z); // 修复平衡
}

修复平衡分析:

插入后分为以下几种情况

1.插入节点的父节点为黑,这种情况直接插入,不用做任何修复

2.插入节点的父节点为红,这种情况需要详细分析

分析第二种插入情况

1.在插入当前节点前,T就是一个红黑树,既满足红黑树左右条件,这是一个很重要的点

2.由第一条结合红黑树特性可以推出,插入节点的祖父节点为黑色

3.插入节点的父节点有可能是祖父的左子树或者右子树,先以左子树为例子进行分析

4.假设插入节点的父节点是344或者374, 插入节点的叔父有两种可能,红或者黑,先说如果叔父为黑的情况,因为在插入该节点前为红黑树,所以如果在叔父为黑且不为叶子节点的时候,是不可能达成红黑树特性的,因为插入节点的父节点为红,插入节点的叔父节点和父节点在插入节点的祖父向上,父节点和祖父节点的黑高肯定一样,假设为x,因为父节点有两个黑的子节点,所以黑高为x+1,如果叔父为子节点,那么叔父那边的黑高也为x+1,符合红黑树性质,如果叔父为黑,且不为叶子,那么黑节点的叶子也为黑,叔父这边的黑高肯定大于x+1,所以得出结论:如果叔父节点为黑,必为叶子节点

5.第四步分析了叔父为黑的情况,如果叔父节点为红色,那么需要做的是重新染色,保证黑高和没有连续红节点,做法是:父和叔父染为黑色,祖父染为红色,然后需要将祖父设置为需要修复的节点,递归像上处理

6.叔父节点为黑色的时候,如果添加的节点在右子树,先对添加节点的父进行左旋,然后重新染色,在对祖父节点进行右旋

void rbtree_insert_fixup(rbtree *T, rbtree_node *z) {

	while (z->parent->color == RED) { //z ---> RED
		if (z->parent == z->parent->parent->left) {
			rbtree_node *y = z->parent->parent->right;
			if (y->color == RED) {
				z->parent->color = BLACK;
				y->color = BLACK;
				z->parent->parent->color = RED;

				z = z->parent->parent; //z --> RED
			} else {

				if (z == z->parent->right) {
					z = z->parent;
					rbtree_left_rotate(T, z);
				}

				z->parent->color = BLACK;
				z->parent->parent->color = RED;
				rbtree_right_rotate(T, z->parent->parent);
			}
		}else {
			rbtree_node *y = z->parent->parent->left;
			if (y->color == RED) {
				z->parent->color = BLACK;
				y->color = BLACK;
				z->parent->parent->color = RED;

				z = z->parent->parent; //z --> RED
			} else {
				if (z == z->parent->left) {
					z = z->parent;
					rbtree_right_rotate(T, z);
				}

				z->parent->color = BLACK;
				z->parent->parent->color = RED;
				rbtree_left_rotate(T, z->parent->parent);
			}
		}
		
	}

	T->root->color = BLACK;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值