Loading

红黑树介绍

红黑树

1 红黑树的引入

有了二叉搜索树,为什么还需要平衡二叉树?

  1. 二叉搜索树容易退化成一条链,
  2. 这个时候查找时间复杂度从 \(O(log_2N)\) 退化成 \(O(N)\)
  3. 引入对左右子树高度差有限制的平衡二叉树,保证查找操作的最坏时间复杂度也为 \(O(log_2N)\)

有了平衡二叉树,为什么还需要红黑树?

  1. \(AVL\) 的左右子树高度差不能超过1,每次进行插入/删除操作时,几乎都需要通过旋转操作保持平衡
  2. 在频繁进行插入/删除的场景中,频繁的旋转操作使得 \(AVL\) 的性能大打折扣
  3. 红黑树通过牺牲严格的平衡,换取插入/删除时少量的旋转操作,整体性能 优于 \(AVL\)
    1. 红黑树插入时的不平衡,不超过两次旋转就可以解决;删除时的不平衡,不超过三次旋转就能解决
  4. 红黑树的红黑规则,保证最坏的情况下,也能在 \(O(log_2N)\) 时间内完成查找操作

红黑树的应用

  1. Linux 进程调度 CFS
  2. Nginx Timer 时间管理
  3. Epoll 事件块的管理

红黑树的性质 (红黑规则)

  1. 每个节点不是黑的就是红的
  2. 根节点是黑的
  3. 每个叶子节点是黑的
  4. 如果一个节点是红的,则它的两个孩子都是黑的
  5. 每个节点到叶子节点的所有路径,都包含相同数目的黑色节点(相同的黑色高度)

image

一些说明

  1. 约束4和5,保证了红黑树的大致平衡:根到叶子的所有路径中,最长路径不会超过最短路径的2倍。
  2. 使得红黑树在最坏的情况下,也能有 \(O(log_2N)\)
  3. 默认新插入的节点为红色:因为父节点为黑色的概率较大,插入新节点为红色,可以避免颜色冲突
  4. 黑色叶子节点指的是 NULL

2 红黑树的左旋和右旋

红黑树左右旋转是为了恢复黑高

2.1 红黑树的定义

typedef int KEY_TYPE;

#define RED	1 // 黑
#define BLACK 2 // 红
// 定义节点
typedef struct _rbtree_node {
	unsigned char color; // 颜色
	struct _rbtree_node *right; // 左子树
	struct _rbtree_node *left; // 右子树
	struct _rbtree_node *parent; // 父亲
	KEY_TYPE key; // 键
	void *value; // 值
} rbtree_node;

// 定义树
typedef struct _rbtree {
	rbtree_node *root; // 跟节点
	rbtree_node *nil; // 小技巧,统一空的叶子节点
} rbtree;
  • 当红黑规则不满足时,需要对节点进行变色或旋转操作

2.2 左右旋转

image

左旋代码:

主要思想:右孩子变爹

主要步骤:

  1. 备份右孩子
  2. 处理新右孩子
  3. 给原有孩子认新爹
  4. 处理处理旋转好后的关系
// T 指的是树, x(原爹)指的对应上图,即轴节点
void rbtree_left_rotate(rbtree *T, rbtree_node *x) {
	// 1. 记录 x 的右孩子 y
	rbtree_node *y = x->right;
	
    // 2. 处理新右孩子
	x->right = y->left; // 然后先处理 x 的右孩子, 即赋值为新的 y->left
	if (y->left != T->nil) { // 如果 y->left 存在,记得给他认一下爹,因为红黑树记录了父亲是谁
		y->left->parent = x;
	}

    // 3. 给 y(原右孩子) 认新爹
	y->parent = x->parent; // x 的爹现在应该是 y 的爹
	if (x->parent == T->nil) { // 如果 x 是根节点,那它没有爹,那么 y 也没有爹,直接变成根节点
		T->root = y;
    // 否则判断一下是 x 是它爹的左孩子还是右孩子,相应的给 y 认好爹
	} else if (x == x->parent->left) { 
		x->parent->left = y;
	} else {
		x->parent->right = y;
	}
	
    // 4 处理一下旋转好后的 x(原爹) 和 y(新爹) 的关系
	y->left = x; // x 是 y 的左孩子
	x->parent = y; // x 的爹是 y
}

右旋代码:

技巧: 将左旋的所有left 和 right 互换, x 和 y 互换 即可,因为本来就是镜像的

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

    // 1. 备份左孩子
	rbtree_node *x = y->left;

    // 2. 处理新左孩子
	y->left = x->right;
	if (x->right != T->nil) {
		x->right->parent = y;
	}

    // 3. 认爹
	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;
	}

    // 4. 调节
	x->right = y;
	y->parent = x;
}

3 红黑树插入

口诀:左旋/右旋,三个方向,六个指针;

插入的颜色默认是红色,因为红色不会改变黑的高度。
违背的是性质4,当前节点是红色,父节点也是红色。

3.1 先插入

先将新节点插入,主要分为五步:

  1. 设置节点
  2. 找爹
  3. 认孩子
  4. 完善信息
  5. 调整树形
// T 是树, z是要插入的节点
void rbtree_insert(rbtree *T, rbtree_node *z) {

    // 1. 设置节点
	rbtree_node *y = T->nil; // y 表示为 z 的爹,默认为 nil
	rbtree_node *x = T->root; // x 表示为根节点

    // 2. 找爹
    // 找插入的位置, 即找 z 的爹
	while (x != T->nil) { // x 存在
		y = x; // 用 y 记录 z 的爹
		if (z->key < x->key) { // 在根左
			x = x->left;
		} else if (z->key > x->key) { // 在根右
			x = x->right;
		} else { // 已经存在了,不要插了,滚
			return ;
		}
	}

    // 3. 认孩子
	z->parent = y;
	if (y == T->nil) { // 没有爹,说明 z 是根节点,即是个空树
		T->root = z;
    // 有爹,判断自己是爹的左孩子还是右孩子
	} else if (z->key < y->key) {
		y->left = z;
	} else {
		y->right = z;
	}

    // 4. 登记户口,完善信息
    // 完善 z 的基本信息
	z->left = T->nil;
	z->right = T->nil;
	z->color = RED;

    // 5. 调整
	rbtree_insert_fixup(T, z);
}

3.2 再调整

父亲是祖父的左孩子:调整分三个情况

  1. 叔节点是红色(直接处理)

    1. 爹、叔叔变黑,爷爷变红
  2. 叔节点是黑色,当前节点是 右孩子 (左旋爹变成情况3处理)

  3. 叔节点是黑色,当前节点是 左孩子 (直接处理)

    1. 爹变黑,爷爷变红,右旋爷爷(目的是为了不改变右子树的黑高,因为爷爷变红了,右子树黑高变低了)

父亲是祖父的右孩子:和左旋右旋的规律一样,左边右,右边左,就行了,还是这三个情况,只不过是镜像的

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; // y是叔节点
			// 情况1,直接搞
            if (y->color == RED) { 
				z->parent->color = BLACK; // 父亲变黑
				y->color = BLACK; // 叔叔变黑
				z->parent->parent->color = RED; // 祖父变红

				z = z->parent->parent; //z --> RED
            // 情况2,3
			} else {
				// 情况2,左旋爹变成情况3
				if (z == z->parent->right) {
					z = z->parent;
					rbtree_left_rotate(T, z);
				}
				// 情况3,直接搞
				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;
}

4 红黑树的删除

和插入如出一辙,也可以说简单了,因为虽然是4种情况,实际都是为了最后一种做变换, 其他啥也不是,非常简单,背就完了。

4.1 认清情况

对了,要分清楚我们真正删除的是哪一个就好理解了,例如我们想删除 z = 172,我们是用 y 的值覆盖了 z, 然后调整了 x 的位置。如下图:

image

4.1 先删除

  1. 判断z是不是俩孩子都在,都在就得找后继节点y
    1. 不是都在就 y = z直接删了
  2. 找到要用来调整的轴心节点 x, 即 y 的孩子
rbtree_node *rbtree_delete(rbtree *T, rbtree_node *z) {

	rbtree_node *y = T->nil; // y 是要删除的节点
	rbtree_node *x = T->nil; // x 是要调整的轴心节点

    // 如果不是左右孩子都存在,需要找后继节点
    // 找中序遍历的后继节点,剑指offer原题,即y, 也就是我们要删除的节点
	if ((z->left == T->nil) || (z->right == T->nil)) {
		y = z;
	} else {
		y = rbtree_successor(T, z);
	}

    // 找到 y 的孩子,也就是 x(要调整的轴心节点)
	if (y->left != T->nil) {
		x = y->left;
	} else if (y->right != T->nil) {
		x = y->right;
	}

    // 安顿好 x 的新爹
	x->parent = y->parent;
	if (y->parent == T->nil) {
		T->root = x;
	} else if (y == y->parent->left) {
		y->parent->left = x;
	} else {
		y->parent->right = x;
	}

    // 这里只有在 z 的左右孩子都有的时候才会满足
	if (y != z) {
		z->key = y->key; // 覆盖节点z
		z->value = y->value;
	}

    // 只有当 y 是黑才调整,因为删除一个红色节点又不影响黑高
	if (y->color == BLACK) {
		rbtree_delete_fixup(T, x); // 注意要调整的是谁
	}

	return y; // 返回删除的那个节点,到时候你想free,想干啥都行
}

上面用到的辅助函数:

// 找最左,即最小的那个点
rbtree_node *rbtree_mini(rbtree *T, rbtree_node *x) {
	while (x->left != T->nil) {
		x = x->left;
	}
	return x;
}

// 找中序遍历的下一个节点
rbtree_node *rbtree_successor(rbtree *T, rbtree_node *x) {
	rbtree_node *y = x->parent;

    // 先找右子树上最左的
	if (x->right != T->nil) {
		return rbtree_mini(T, x->right);
	}

    // 如果没有右子树,找到第一个是左孩子的爹就行
	while ((y != T->nil) && (x == y->right)) { // 只要还是右孩子就一直找,找到是左孩子为止
		x = y;
		y = y->parent;
	}
	return y;
}

4.2 再调整

注意要调整的是 x, 然后就没啥了

当前节点是左子树:调整分四个情况

  1. 兄弟是红色(调整为情况2,3或4)

    1. 兄弟变黑,父节点变红,左旋父亲,更新兄弟

      image

  2. 兄弟是黑色,兄弟的俩孩子都是黑的(调整为情况3或4)

    1. 兄弟变红,x指向父节点

      image

  3. 兄弟是黑的,兄弟的左孩子是红的,右孩子是黑的(调整为情况4)

    1. 左侄子变黑,兄弟变红,右旋兄弟,更新兄弟

    image

  4. 兄弟是黑的,兄弟的右孩子是红色的,直接干

    1. 兄父同色,右侄子父亲都变黑,左旋父亲,x指向根节点结束

    image

void rbtree_delete_fixup(rbtree *T, rbtree_node *x) {
	// x不是根节点,x是黑才调整
	while ((x != T->root) && (x->color == BLACK)) {
		if (x == x->parent->left) {// 是左子树

			rbtree_node *w= x->parent->right; // 兄弟
            // 情况1,调整为情况2、3或4
			if (w->color == RED) {
				w->color = BLACK; // 兄黑
				x->parent->color = RED; // 父红

				rbtree_left_rotate(T, x->parent); // 左旋父亲
				w = x->parent->right; // 更新兄弟,左侄子变右兄弟了
			}
			
            // 情况2,调整为情况3或4
			if ((w->left->color == BLACK) && (w->right->color == BLACK)) {
				w->color = RED; // 兄弟变红
				x = x->parent; // x 指向父节点
			} else {
				
                // 情况3,调整为情况4
				if (w->right->color == BLACK) {
					w->left->color = BLACK; // 兄弟左儿子变黑
					w->color = RED; // 兄弟变红
					rbtree_right_rotate(T, w); // 右旋兄弟
					w = x->parent->right; // 更新兄弟,左侄子成兄弟了
				}

                // 情况4, 直接干
				w->color = x->parent->color; // 兄父同色
				x->parent->color = BLACK; // 父亲变黑
				w->right->color = BLACK; // 右侄子变黑
				rbtree_left_rotate(T, x->parent); // 左旋

				x = T->root; // x指向根节点,结束了
			}
		
        // 和上面是镜像的
		} else {

			rbtree_node *w = x->parent->left;
			if (w->color == RED) {
				w->color = BLACK;
				x->parent->color = RED;
				rbtree_right_rotate(T, x->parent);
				w = x->parent->left;
			}

			if ((w->left->color == BLACK) && (w->right->color == BLACK)) {
				w->color = RED;
				x = x->parent;
			} else {

				if (w->left->color == BLACK) {
					w->right->color = BLACK;
					w->color = RED;
					rbtree_left_rotate(T, w);
					w = x->parent->left;
				}

				w->color = x->parent->color;
				x->parent->color = BLACK;
				w->left->color = BLACK;
				rbtree_right_rotate(T, x->parent);

				x = T->root;
			}

		}
	}

	x->color = BLACK;
}

5 总结

  1. 调整的原因都是因为违背了性质4或性质5;

    1. 插入是红色,所以可能违背性质4
      1. 插入的时候情况2是为了情况3做准备
    2. 删除可能删除了黑节点,所以可能改变黑高违背性质5
      1. 删除调整的时候都是在为情况4做准备
  2. 左旋右旋的目的都是因为黑高改变了需要调整,不信你仔细思考一下,思考不明白文章最后有个很好的参考链接,去看看就知道了

  3. 另一种都是镜像的,只需要记住一种即可

参考链接

参考链接

总代码




#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define RED				1
#define BLACK 			2

typedef int KEY_TYPE;

typedef struct _rbtree_node {
	unsigned char color;
	struct _rbtree_node *right;
	struct _rbtree_node *left;
	struct _rbtree_node *parent;
	KEY_TYPE key;
	void *value;
} rbtree_node;

typedef struct _rbtree {
	rbtree_node *root;
	rbtree_node *nil;
} rbtree;

rbtree_node *rbtree_mini(rbtree *T, rbtree_node *x) {
	while (x->left != T->nil) {
		x = x->left;
	}
	return x;
}

rbtree_node *rbtree_maxi(rbtree *T, rbtree_node *x) {
	while (x->right != T->nil) {
		x = x->right;
	}
	return x;
}

rbtree_node *rbtree_successor(rbtree *T, rbtree_node *x) {
	rbtree_node *y = x->parent;

	if (x->right != T->nil) {
		return rbtree_mini(T, x->right);
	}

	while ((y != T->nil) && (x == y->right)) {
		x = y;
		y = y->parent;
	}
	return y;
}


void rbtree_left_rotate(rbtree *T, rbtree_node *x) {

	rbtree_node *y = x->right;  // x  --> y  ,  y --> x,   right --> left,  left --> right

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

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

	y->left = x; //1 5
	x->parent = y; //1 6
}


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_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; // y是叔节点
			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;
}


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);
}

void rbtree_delete_fixup(rbtree *T, rbtree_node *x) {

	while ((x != T->root) && (x->color == BLACK)) {
		if (x == x->parent->left) {

			rbtree_node *w= x->parent->right;
			if (w->color == RED) {
				w->color = BLACK;
				x->parent->color = RED;

				rbtree_left_rotate(T, x->parent);
				w = x->parent->right;
			}

			if ((w->left->color == BLACK) && (w->right->color == BLACK)) {
				w->color = RED;
				x = x->parent;
			} else {

				if (w->right->color == BLACK) {
					w->left->color = BLACK;
					w->color = RED;
					rbtree_right_rotate(T, w);
					w = x->parent->right;
				}

				w->color = x->parent->color;
				x->parent->color = BLACK;
				w->right->color = BLACK;
				rbtree_left_rotate(T, x->parent);

				x = T->root;
			}

		} else {

			rbtree_node *w = x->parent->left;
			if (w->color == RED) {
				w->color = BLACK;
				x->parent->color = RED;
				rbtree_right_rotate(T, x->parent);
				w = x->parent->left;
			}

			if ((w->left->color == BLACK) && (w->right->color == BLACK)) {
				w->color = RED;
				x = x->parent;
			} else {

				if (w->left->color == BLACK) {
					w->right->color = BLACK;
					w->color = RED;
					rbtree_left_rotate(T, w);
					w = x->parent->left;
				}

				w->color = x->parent->color;
				x->parent->color = BLACK;
				w->left->color = BLACK;
				rbtree_right_rotate(T, x->parent);

				x = T->root;
			}

		}
	}

	x->color = BLACK;
}

rbtree_node *rbtree_delete(rbtree *T, rbtree_node *z) {

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

	if ((z->left == T->nil) || (z->right == T->nil)) {
		y = z;
	} else {
		y = rbtree_successor(T, z);
	}

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

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

	if (y != z) {
		z->key = y->key;
		z->value = y->value;
	}

	if (y->color == BLACK) {
		rbtree_delete_fixup(T, x);
	}

	return y;
}

rbtree_node *rbtree_search(rbtree *T, KEY_TYPE key) {

	rbtree_node *node = T->root;
	while (node != T->nil) {
		if (key < node->key) {
			node = node->left;
		} else if (key > node->key) {
			node = node->right;
		} else {
			return node;
		}	
	}
	return T->nil;
}


void rbtree_traversal(rbtree *T, rbtree_node *node) {
	if (node != T->nil) {
		rbtree_traversal(T, node->left);
		printf("key:%d, color:%d\n", node->key, node->color);
		rbtree_traversal(T, node->right);
	}
}

int main() {

	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;

	rbtree_node *node = T->nil;
	int i = 0;
	for (i = 0;i < 20;i ++) {
		node = (rbtree_node*)malloc(sizeof(rbtree_node));
		node->key = keyArray[i];
		node->value = NULL;

		rbtree_insert(T, node);
		
	}

	rbtree_traversal(T, T->root);
	printf("----------------------------------------\n");

	for (i = 0;i < 20;i ++) {

		rbtree_node *node = rbtree_search(T, keyArray[i]);
		rbtree_node *cur = rbtree_delete(T, node);
		free(cur);

		rbtree_traversal(T, T->root);
		printf("----------------------------------------\n");
	}
	

	
}






posted @ 2022-06-08 22:07  Christopher_James  阅读(100)  评论(0)    收藏  举报