博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
红黑树的自旋转和结点插入操作的解析
阅读量:2432 次
发布时间:2019-05-10

本文共 5777 字,大约阅读时间需要 19 分钟。

红黑树可以实现自平衡,主要依靠于它的旋转和变色的特性

//红黑树节点typedef struct RBTreeNode{
unsigned char color; Type key; struct RBTreeNode* left; struct RBTreeNode* right; struct RBTreeNode* parent;}Node, *RBTree;//红黑树的根typedef struct rb_root{
Node* node;}RBRoot;

左旋:以某个节点作为旋转节点,其右子节点变为旋转节点的父节点,右子节点的左子节点变为旋转节点的右子节点,旋转节点的左子节点保持不变。

static void rbtree_left_rotate(RBRoot* root, Node* x){
//设置x的右孩子为y Node* y = x->right; //将y的左孩子设置为x的右孩子 //如果y的左孩子非空,将x设为y的左孩子的父亲 x->right = y->left; if (y->left != NULL) {
y->left->parent = x; } //将x的父亲设为y的父亲 y->parent = x->parent; //if语句成立说明x为根节点 if (x->parent == NULL) //修改红黑树的根节点 {
root->node = y; //如果“x的父亲”是空节点,则将y设为根节点 } else {
//如果x是它父节点的左孩子,则将y设为“x的父节点” if (x->parent->left == x) {
x->parent->left = y; } //如果x是它父节点的右孩子,则将y设为“x的父节点” else {
x->parent->right = y; } } //将x设为y的左孩子 y->left = x; //将x的父节点设为y x->parent = y;}

右旋:以某个结点作为支点(旋转结点),其左子结点变为旋转结点的父结点,左子结点的右子结点变为旋转结点的左子结点,右子结点保持不变。

static void rbtree_right_rotate(RBRoot* root, Node* y){
//设置x是当前节点的左孩子 Node* x = y->left; //将x的右孩子设为y的左孩子 //如果x的右孩子不为空,将y设为x的右孩子的父亲 y->left = x->right; if (x->right != NULL) {
x->right->parent = y; } //将y的父亲设置为x的父亲 x->parent = y->parent; //if语句成立说明y为根节点 if (y->parent == NULL) {
root->node = x; //如果“y的父亲”是空节点,则将x设为根节点 } else {
//如果y是它父节点的右孩子,则将x设为“y的父节点” if (y->parent->right == y) {
y->parent->right = x; } //如果y是它父节点的左孩子,则将x设为“y的父节点” else {
y->parent->left = x; } } //将y设为x的右孩子 x->right = y; //将y的父节点设为x y->parent = x;}

红黑树的插入操作

情况1:红黑树为空树
直接把插入节点作为根节点,把颜色涂成黑色即可。

void insert_case1(node *n){
if(n->parent == NULL) n->color = BLACK; else insert_case2 (n); }

情况2:插入节点的父节点为黑色节点

因为插入节点是红色的,当插入节点的父节点为黑色节点时,不会对红黑树的平衡造成影响,直接插入即可。

void insert_case2(node *n){
if(n->parent->color == BLACK) return; else insert_case3 (n);}

情况3:插入节点的父节点为红色节点。如果插入的父结点为红结点,那么该父结点不可能为根结点,所以插入结点总是存在祖父结点。

情况3_1:叔叔节点存在且为红色

此时的处理方法是将父亲节点和叔叔节点都涂成黑色,将祖父节点涂成红色,再将祖父节点设置成当前插入的节点,继续从底部向上进行递归或循环判断。

void insert_case3_1(node *n){
if(uncle(n) != NULL && uncle (n)->color == RED) {
n->parent->color = BLACK; uncle (n)->color = BLACK; grandparent (n)->color = RED; insert_case1(grandparent(n));}else insert_case3_2and3_3(n);}

情况3_2:叔叔节点不存在或为黑色节点,并且插入节点的父亲节点是祖父节点的左子节点

情况3_2_1:插入节点是父节点的左子节点

处理方法是将父亲节点设为黑色,祖父节点设为红色,并对祖父节点进行右旋

情况3_3:叔叔节点不存在或为黑色节点,并且插入节点的父亲节点是祖父节点的右子节点

情况3_3_1:插入节点是父节点的右子节点

处理方法是将父亲节点设为黑色,祖父节点设为红色,并对祖父节点进行左旋

void insert_case3_2_1and3_3_1(node *n){
n->parent->color = BLACK; grandparent (n)->color = RED; if(n->parent == grandparent(n)->left && n == n->parent->left) {
rotate_right(grandparent(n)); //情况3_2_1 } else if(n->parent == grandparent (n)->right && n == n->parent->right) {
rotate_left(grandparent(n)); //情况3_3_1 }}

情况3_2:叔叔节点不存在或为黑色节点,并且插入节点的父亲节点是祖父节点的左子节点

情况3_2_2:插入节点是父节点的右子节点

处理方法是对父节点进行左旋,然后情况就变成了3-2-1的情况

情况3_3:叔叔节点不存在或为黑色节点,并且插入节点的父亲节点是祖父节点的右子节点

情况3_3_2:插入节点是父节点的左子节点

处理方法是对父节点进行右旋,然后情况就变成了3_3_1的情况

void insert_case3_2_2and3_3_2(node *n){
if(n->parent == grandparent(n)->left && n == n->parent->right) {
rotate_left(n->parent); //情况3_2_2 n = n->left; } else if(n->parent == grandparent(n)->right && n == n->parent->left) {
rotate_right(n->parent); //情况3_3_2 n = n->right; }}

完整代码如下:

#define rb_parent(r)        ((r)->parent)#define rb_color(r)         ((r)->color)#define rb_is_red(r)        ((r)->color == RED)#define rb_is_black(r)      ((r)->color == BLACK)#define rb_set_black(r)     do {(r)->color = BLACK;} while(0)#define rb_set_red(r)       do {(r)->color = RED;} while(0)#define rb_set_parent(r, p) do {(r)->parent = (p);}while(0)#define rb_set_color(r, c)  do {(r)->color = (c);}while(0)
static void rbtree_insert(RBRoot* root, Node* node){
//添加节点:将节点node插入红黑树中,root为红黑树的根,node为插入的节点 Node* y = NULL; Node* x = root->node; //先将根节点给x //1、将红黑树当做一颗二叉查找树,将节点添加到二叉查找树中 while (x != NULL) {
y = x; //y为所需插入节点的父节点 if (node->key < x->key) {
x = x->left; } else {
x = x->right; } } rb_parent(node) = y; //找到父节点并把要插入节点的父节点的指针修改 //修改父节点的子节点指针 if (y != NULL) {
if (node->key < y->key) {
y->left = node; //情况2:node包含的值 < y所包含的值 } else {
y->right = node; //情况3:node包含的值 >= y所包含的值 } } else {
root->node = node; //情况1:若y是空节点,则将node设为根 } //2、设置节点的颜色为红色 node->color = RED; //3、将它重新修正为一颗rb树 rbtree_insert_fixup(root, node);}
static void rbtree_insert_fixup(RBRoot* root, Node* node){
Node* parent, * gparent; //若父节点存在,且父节点的颜色是红色 while ((parent = rb_parent(node)) && rb_is_red(parent)) {
gparent = rb_parent(parent); //若父节点是祖父节点的左孩子 if (parent == gparent->left) {
//Case1条件:叔叔节点是红色 {
Node* uncle = gparent->right; if (uncle && rb_is_red(uncle)) //若没有节点进入该分支,如何构造 {
rb_set_black(uncle); rb_set_black(parent); rb_set_red(gparent); node = gparent; continue; } } //Case2条件:叔叔是黑色,且当前节点是右孩子,叔叔不存在,也认为是黑色 if (parent->right == node) //插入80节点时,先左旋,后右旋 {
Node* tmp; rbtree_left_rotate(root, parent); tmp = parent; parent = node; node = tmp; } //Case3条件:叔叔是黑色,且当前节点是左孩子 rb_set_black(parent); rb_set_red(gparent); rbtree_right_rotate(root, gparent); } else {
//若父亲节点是祖父节点的右孩子 {
//Case1条件:叔叔节点是红色 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; //继续进行调整 } } //Case2条件:叔叔是黑色或空缺,且当前节点是左孩子,插入30时,先右旋,后左旋 if(parent->left == node) {
Node* tmp; rbtree_right_rotate(root, parent); tmp = parent; parent = node; node = tmp; } //Case3条件:叔叔是黑色或空缺,且当前节点是右孩子 rb_set_black(parent); //旋转前设置好颜色 rb_set_red(gparent); rbtree_left_rotate(root, gparent); } } //将根节点设为黑色 rb_set_black(root->node);}

转载地址:http://sxxmb.baihongyu.com/

你可能感兴趣的文章
RPC实践(三)Hessian实践
查看>>
Zookeeper实践(四)zookeeper的WEB客户端zkui使用
查看>>
RPC实践(五)Dubbo实践-服务集群
查看>>
java单元测试Junit实践(一) Junit基础
查看>>
Webservice实践(二)Webservice 客户端开发
查看>>
Webservice实践(三)基于JDK的jax ws进行服务端开发
查看>>
Webservice实践(四)基于AXIS2的服务端开发
查看>>
Ubuntu12.04下安装eclipse C/C++开发环境
查看>>
Eclipse中10个最有用的快捷键组合
查看>>
Routing
查看>>
json相关学习
查看>>
linux下access函数的应用
查看>>
linux系统调用之文件:递归删除非空目录
查看>>
linux下获取系统时间的方法
查看>>
ubuntu12.04安装openCV2.4.6.1
查看>>
jsp与servlet的作用以及区别--为什么说JSP底层就是一个Servlet
查看>>
看HashMap源码前的必备冷知识,白话文式教学,适合刚开始了解源码的新手观看
查看>>
Oracle安装指南
查看>>
Redis面试必备(一)
查看>>
Cookie对象入门详解
查看>>