完整的红黑树学习路线指导

5 篇文章 0 订阅
4 篇文章 0 订阅

红黑树学习路线

Author:yangzhichao passion_yang_008.163.com

​ 网上有很多博客讲红黑树,有的讲的很清晰,有的不清晰,就算你找到一个有各种插图,讲的很清晰的博客,但对于首次接触红黑树的你来说,只是看文字说明和插图来书,还是有点吃力,所以本人亲自总结了一套学习计划,保证你在四天之内学完红黑树(如果你看本人的博客,还学不会,本人挥刀自宫),并能写出实用的c语言代码。

  • 首先建议观看关于如何构建红黑树,了解什么是平衡二叉树,对于不平衡的二叉树,如何将它调整为平衡二叉树,构建红黑树等视频,视频链接地址如下:
		https://edu.csdn.net/course/detail/26202
  • 在旧金山大学的算法学习网站上模拟红黑树的插入,删除,旋转等操作,这个对你后面编写红黑树来说非常重要,链接地址如下:
		https://www.cs.usfca.edu/~galles/visualization/RedBlack.html
  • 当学习玩如何构建红黑树之后,请查看博客系列:
		https://www.cnblogs.com/skywang12345/p/3624177.html#a1
  • 认真查看下面的代码,保证你学会红黑树,你会感谢我的,嘿嘿。

红黑树的性质

  • 节点是红色或者黑色。

  • 根节点是黑色

  • 每个叶子节点都是黑色的空节点

  • 每个红色节点的两个叶子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)。

  • 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。

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

  • 红黑树放弃了追求完全的平衡,只只求大致的平衡,在与平衡二叉树的时间复杂度相差不大的情况下,保证每次插入最多只需要三次旋转就能达到平衡,实现起来比较简单。

  • 平衡二叉树追求绝对的平衡,条件苛刻,实现起来比较麻烦,每次插入新结点之后需要旋转的次数不能预知。

红黑树的实现

  • rb_tree.h 头文件
#pragma once
#ifndef _RB_TREE_H
#define _RB_TREE_H

#define RB_RED		0	/* 红色节点 */
#define RB_BLACK	1	/* 黑色节点 */

typedef struct RBTreeNode
{
	unsigned char color;
	int key;
	struct RBTreeNode * left;
	struct RBTreeNode * right;
	struct RBTreeNode * parent;
}rb_node,*rb_node_t;


typedef struct rb_root
{
	rb_node * node;
}rb_root;


rb_root * create_rbtree();

void print_rbtree(rb_root * root);

void insert_rbtree(rb_root *root,rb_node * node);

void rbtree_delete(rb_root *root,rb_node * node);

void rbtree_inorder(rb_node * node);

rb_node * rbtree_search(rb_root *root, int key);

#endif /*  _RB_TREE_H */

  • 红黑树插入后着色和调整思想
现象说明处理策略
Case 1当前节点的父节点是红色,且当前节点的祖父节点的另一个子节点(叔叔节点)也是红色。(01) 将“父节点”设为黑色。 (02) 将“叔叔节点”设为黑色。 (03) 将“祖父节点”设为“红色”。 (04) 将“祖父节点”设为“当前节点”(红色节点);即,之后继续对“当前节点”进行操作。
Case 2当前节点的父节点是红色,叔叔节点是黑色,且当前节点是其父节点的右孩子(01) 将“父节点”作为“新的当前节点”。 (02) 以“新的当前节点”为支点进行左旋。
Case 3当前节点的父节点是红色,叔叔节点是黑色,且当前节点是其父节点的左孩子(01) 将“父节点”设为“黑色”。 (02) 将“祖父节点”设为“红色”。 (03) 以“祖父节点”为支点进行右旋。
  • 红黑树删除节点思想
现象说明处理策略
Case 1x是"黑+黑"节点,x的兄弟节点是红色。(此时x的父节点和x的兄弟节点的子节点都是黑节点)。(01) 将x的兄弟节点设为“黑色”。 (02) 将x的父节点设为“红色”。 (03) 对x的父节点进行左旋。 (04) 左旋后,重新设置x的兄弟节点。
Case 2x是“黑+黑”节点,x的兄弟节点是黑色,x的兄弟节点的两个孩子都是黑色。(01) 将x的兄弟节点设为“红色”。 (02) 设置“x的父节点”为“新的x节点”。
Case 3x是“黑+黑”节点,x的兄弟节点是黑色;x的兄弟节点的左孩子是红色,右孩子是黑色的。(01) 将x兄弟节点的左孩子设为“黑色”。 (02) 将x兄弟节点设为“红色”。 (03) 对x的兄弟节点进行右旋。 (04) 右旋后,重新设置x的兄弟节点。
Case 4x是“黑+黑”节点,x的兄弟节点是黑色;x的兄弟节点的右孩子是红色的,x的兄弟节点的左孩子任意颜色。(01) 将x父节点颜色 赋值给 x的兄弟节点。 (02) 将x父节点设为“黑色”。 (03) 将x兄弟节点的右子节设为“黑色”。 (04) 对x的父节点进行左旋。 (05) 设置“x”为“根节点”。
  • 红黑树实现 rb_tree.c
#include "rb_tree.h"
#include <stdio.h>

#define	MAXSIZE		1024
#define rb_is_red(x) ((x)->color == RB_RED)
#define rb_is_black(x) ((x)->color == RB_BLACK)
#define rb_set_black(x) do{(x)->color = RB_BLACK;}while(0)
#define rb_set_red(x) do{(x)->color = RB_RED;}while(0)

rb_root * create_rbtree()
{
	rb_root * root = (rb_root *)malloc(sizeof(rb_root));
	root->node = NULL;
	return root;
}
/* 中序非递归遍历算法 */
void rbtree_inorder(rb_node * node)
{
	rb_node *buf[MAXSIZE];
	rb_node * p = NULL;
	int top = -1;
	p = node;
	while (-1 != top || NULL != p) {
		/*判断左子树是否为空,将所有的左子树入栈 */
		while (NULL != p) {
			buf[++top] = p;
			p = p->left;
		}
		if(-1 != top){	/* 判断栈中是否有元素 */
			p = buf[top--];/* 出栈打印元素 */
			printf("\t %d\t %s \n", p->key, (p->color == 1 ? "black" : "red"));
			p = p->right;
		}
	}	
}
void print_rbtree(rb_root * root)
{
	if (root->node)
		inorder(root->node);
}
void rbtree_right_rotate(rb_root *root, rb_node * node)
{
	rb_node * x = node->left;
	/* 先将x 的右孩子给node */
	node->left = x->right;
	/* 让node给 x的右子树当爸爸 */
	if(NULL != x->right)
		x->right->parent = node;
	/*将 node的爸爸给 x当爸爸 */
	x->parent = node->parent;
	/* 判断node是不是没有爸爸 */
	if (NULL == node->parent) {
		root->node = x;
	}
	else {
		/* 查看 node是自己父亲的左儿子还是右儿子*/
		if (node == node->parent->left)
			node->parent->left = x;
		else
			node->parent->right = x;
	}
	/*由于是右旋,所以node现在变成了自己左儿子的有儿子 */
	x->right = node;
	/* node当初自己的右儿子变成了自己的爸爸 乱伦呀 */
	node->parent = x;
}
void rbtree_left_rotate(rb_root *root, rb_node * node)
{
	rb_node * x = node->right;
	/*先拿出被旋转的两个元素 */
	/* 先将可能发生冲突的两个元素处理,x的左子树旋转过后会变为 node的右子树 */
	node->right = x->left;
	/* 判断存在 */
	if (NULL != x->left)
		x->left->parent = node;
	/* 将 node的父亲交给 x */
	x->parent = node->parent;
	/*判断父亲是都存在 */
	if (NULL == node->parent) {
		root->node = x;
	}
	else {
		/*判断 node是父亲的左子树还是父亲的右子树 */
		if (node == node->parent->right)
			node->parent->right = x;
		else
			node->parent->left = x;
	}
	/* node 变成了自己有孩子的 左孩子 */
	x->left = node;
	/* 现在 node自己的右孩子x变成了自己的爸爸 */
	node->parent = x;
}
void rbtree_insert_fixup(rb_root *root, rb_node * node)
{
	rb_node *parent = NULL;
	rb_node *gparent = NULL;
	while ((parent = node->parent) && rb_is_red(parent)) {
		gparent = parent->parent;
		/* 如果 parent 是 gparent的左子树 */
		if (parent == gparent->left) {
			/*  第一种情况,当叔叔节点是红色时 */
			{
				rb_node * uncle = gparent->right;
				if (uncle && rb_is_red(uncle)) {
					rb_set_black(parent);
					rb_set_black(uncle);
					rb_set_red(gparent);
					node = gparent;
					continue;
				}
			}
			/*
			*      px                              px
			*     /   \                            / \
			*    x     ub                         y   ub             
			*   /  \      --(左旋)-->           / \           
			*  lx   y                          x  ry     
			*     /   \                       /  \
			*    ly   ry                     lx  ly  
			*								 恰好是第三种情况
			*/
			/* 当前节点是父亲节点的右子树时,先将这种情况化简为第三种情况 */
			if (node == parent->right) {
				rb_node * tmp;
				rbtree_left_rotate(root, parent);
				tmp = parent;
				parent = node;
				node = tmp;
			}
			rb_set_black(parent);
			rb_set_red(gparent);
			rbtree_right_rotate(root, gparent);
		}
		/* parent 是gparent 的右孩子 */
		else {
			/*第一种情况 */
			{
				rb_node * uncle = gparent->left;
				if (uncle && rb_is_red(uncle)) {
					rb_set_black(uncle);
					rb_set_black(parent);
					rb_set_red(gparent);
					node = gparent;
					continue;
				}
			}
			/*处理第二种情况,方法是:旋转为第三种情况 */
			if (node == parent->left) {
				rb_node * tmp;
				rbtree_right_rotate(root, parent);
				tmp = parent;
				parent = node;
				node = tmp;
			}
			/* 处理第三种情况 */
			rb_set_black(parent);
			rb_set_red(gparent);
			rbtree_left_rotate(root,gparent);
		}
	}
	/* 将根节点染成黑色 */
	root->node->color = RB_BLACK;
}
void insert_rbtree(rb_root *root, rb_node * node)
{
	rb_node * y = NULL;
	rb_node * x = root->node;
	/* 在哪里插入 */
	while(x != NULL){
		y = x;
		if (node->key < x->key)
			x = x->left;
		else
			x = x->right;
	}
	/* 插入操作 */
	node->parent = y;
	/*判断是否是头结点 */
	if (NULL == y)
		root->node = node;
	else {
		/* 判断是大于当前值还是小于当前值 */
		if (node->key < y->key)
			y->left = node;
		else
			y->right = node;
	}
	/* 默认插入为红色 */
	node->color = RB_RED;
	/* 插入调整颜色和位置 */
	rbtree_insert_fixup(root,node);
}
static void 
rbtree_delete_fixup(rb_root *root, rb_node * node, rb_node * parent)
{
	rb_node * other = NULL;
	/*分为两种情况,node不是头结点 */
	while((!node || rb_is_black(node)) && (node != root->node)){
		/*node是parent的左子树*/
		if (node == parent->left) {
			/* 先判断兄弟节点 */
			other = parent->right;
			/* 兄弟节点是红色 case 1: */
			if (rb_is_red(other)) {
				rb_set_black(other);
				rb_set_red(parent);
				rbtree_left_rotate(root,parent);
				other = parent->right;
			}
			/* 兄弟节点是黑色,并且兄弟节点的两个孩子也是黑色 case 2:*/
			if (((!other->left) || rb_is_black(other->left)) &&
				(!other->right || rb_is_black(other->right))) {
				rb_set_red(other);
				node = parent;
				parent = node->parent;
			}
			else {
				if (!other->right || rb_is_black(other->right)) {
					/*兄弟节点是黑色 左孩子是红色右孩纸是黑色 case 3:*/
					rb_set_black(other->left);
					rb_set_red(other);
					rbtree_right_rotate(root,other);
					other = parent->right;
				}
				/*兄弟节点是黑色 左孩子是黑色右孩纸是红色 case 4*/
				other->color = parent->color;
				rb_set_black(parent);
				rb_set_black(other->right);
				rbtree_left_rotate(root,parent);
				node = root->node;
			}
		}
		/* node 是parent 的右子树 */
		else {
			other = parent->left;
			/* 第一种情况 */
			if (rb_is_red(other)) {
				rb_set_black(other);
				rb_set_red(parent);
				rbtree_right_rotate(root,parent);
				other = parent->left;
			}
			/* case 2*/
			if (((!other->left) || rb_is_black(other->left)) &&
				(!other->right || rb_is_black(other->right))) {
				rb_set_red(other);
				node = parent;
				parent = node->parent;
			}
			else {
				if (!other->left || rb_is_black(other->left)) {
					/*兄弟节点是黑色 右孩子是红色左孩纸是黑色 case 3:*/
					rb_set_black(other->right);
					rb_set_red(other);
					rbtree_left_rotate(root, other);
					other = parent->left;
				}
				/* case 4*/
				other->color = parent->color;
				rb_set_black(parent);
				rb_set_black(other->left);
				rbtree_right_rotate(root, parent);
				node = root->node;
			}
		}
	}
	/*node 是头结点 */
	if (node)
		rb_set_black(node);
}
void rbtree_delete(rb_root *root, rb_node * node)
{
	rb_node * child, *parent;
	unsigned char color;
	/* 如果删除的左子树和右子树都不为空 */
	if ((NULL != node->left) && (NULL != node->right)) {
		/* 查找替换元素 */
		rb_node * replace = node;/*记录下node节点 */
		replace = replace->left;
		while (NULL != replace->right)
			replace = replace->right;
		/* 查找到replace 后 */
		parent = node->parent;//记录node父节点
		if (parent) {
			/*父节点存在,判断node是左子树还是右子树 */
			if (node == parent->right)
				parent->right = replace;
			else
				parent->left = replace;
		}
		else
			root->node = replace;

		/*记录replace的父节点 */
		parent = replace->parent;
		/*记录replace的左孩子*/
		child = replace->left;
		/* 记录replace的颜色 */
		color = replace->color;
		/*判断 parent 是不是和 node 相等 */
		if (parent == node)
			parent = replace;
		else {
			/*替代节点的 孩子存在,也就是替代节点的 左子树存在 */
			if (child) {
				child->parent = parent;
			}
			/*replace的孩子(左子树)成为了replace父节点的右孩子*/
			parent->right = child;
			/*将 replace和 parent链接起来,replace成为了 parent的父节点 */
			replace->left = node->left;
			node->left->parent = replace;
		}
		/*将node中的信息copy到replace中*/
		replace->color = node->color;
		replace->parent = node->parent;
		replace->right = node->right;
		node->right->parent = replace;
		if (color == RB_BLACK) {
			/* 对删除node后,颜色调整 */
			rbtree_delete_fixup(root,child,parent);
		}
		free(node);
		return;
	}
	/* 如果左子树为空 */
	if (NULL != node->left)
		child = node->left;
	else
		child = node->right;
	/* 记录当前节点的父节点 */
	parent = node->parent;
	/* 记录当前节点的颜色 */
	color = node->color;
	/*判断儿子节点是否为空 */
	if(child)
		child->parent = parent;
	/* 判断 node 是不是头结点 */
	if (NULL != node->parent) {
		/* 查看 node 是父节点的左子树还是右子树 */
		if (node == parent->left)
			parent->left = child;
		else
			parent->right = child;
	}
	else
		/* 当前孩子变为头结点 */
		root->node = child;
	if (color == RB_BLACK) {
		/* 对删除node后,颜色调整 */
		rbtree_delete_fixup(root, child, parent);
	}
	free(node);
}
rb_node * rbtree_search(rb_root *root, int key)
{
	rb_node * node = root->node;
	while(NULL != node){
		if (key < node->key)
			node = node->left;
		else if (key > node->key)
			node = node->right;
		else
			return node;
	}
	return NULL;
}

测试程序

#include <stdio.h>
#include "rb_tree.h"

void insert(rb_root * root,int key)
{
	rb_node * node = (rb_node *)malloc(sizeof(rb_node));
	memset(node, 0x00, sizeof(node));
	node->key = key;
	node->left = NULL;
	node->parent = NULL;
	node->right = NULL;
	insert_rbtree(root, node);
}
rb_root * create(rb_root * root, int * array,int n)
{
	int i = 0;
	for (i = 0; i < n; ++i) {
		insert(root, array[i]);
	}
	return root;
}
void delete(rb_root * root,int key)
{
	rb_node * node = rbtree_search(root,key);
	if (node)
		rbtree_delete(root,node);
}
int main(int argc, char * argv[])
{
	int array[] = {40,10,5,50,45,60,25,74,36,7,3,1,4,8,9};
	rb_root *root = create_rbtree();
	create(root,array,sizeof(array)/sizeof(array[0]));
	print_rbtree(root);

	printf("\n-----------------------------------\n");
	delete(root, 45);

	print_rbtree(root);

	printf("\n-----------------------------------\n");
	delete(root, 8);
	print_rbtree(root);
	printf("\n-----------------------------------\n");
	delete(root, 5);
	print_rbtree(root);

	printf("\n-----------------------------------\n");
	delete(root, 10);
	print_rbtree(root);

	printf("\n-----------------------------------\n");
	delete(root, 9);
	print_rbtree(root);
	printf("\n-----------------------------------\n");
	delete(root, 36);
	print_rbtree(root);
	printf("\n-----------------------------------\n");
	delete(root, 74);
	print_rbtree(root);
	return 0;
}
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值