红黑树的实现

目录

  • 1、红黑树原理
    • 1、红黑树性质
    • 2、变换规则(从插入结点的角度来讲)
      • 1.变色
      • 2.左旋
      • 3.右旋
    • 3、删除结点需要注意的地方
  • 2、代码
    • 1、定义结点以及构造函数
    • 2、定义红黑树类以及声明它的方法
    • 3、左旋
    • 4、右旋
    • 5、插入操作
    • 6、修正操作
    • 7、删除操作
  • 3、参考链接

1、红黑树原理

为了避免二叉搜索树出现退化为链表的情况,出现了自平衡的二叉搜索树。
AVL树:平衡二叉树,它的左右子树高度之差不超过1,不过它太过于理想化。
通过性能综合考虑选用红黑树。

1、红黑树性质

1、每个结点不是红色就是黑色
2、不可能有连在一起的红色结点
3、根结点都是黑色
4、每个红色结点的两个子结点都是黑色。
5、叶子结点都是黑色(叶子结点指的是NULL结点)
6、 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。

2、变换规则(从插入结点的角度来讲)

变换规则分为三种:

变色、左旋、右旋

1.变色

变色的前提:当前结点的父结点是红色,且它的祖父结点的另一个结点==(叔结点)也是红色==。
变色的过程:

1、将父结点设为黑色
2、叔结点设为黑色
3、祖父结点设为红色
4、把指针定义到祖父结点设为当前要操作的结点。
5、继续分析判断

示例如下:
按照二叉搜索树的规则插入对应的结点6;发现违反规则了,两个红色的连到一起了,并且符合变色的前提,所以进行变色

图1 插入结点6,将其作为当前结点
图2 做完变色变换后,将之前的祖父结点作为当前结点

2.左旋

观察变色之后的树,发现仍然是违反规则的:5、12两个红色连在一起了。
然后再看是不是符合变色的前提:显然不符合,因为12的叔结点30不是红色。那么这时就应该判断是否符合左旋的前提了。
左旋的前提:
当前父结点是红色,叔叔是黑色的时候,且当前的结点是右子树。
左旋的过程:

以父结点作为中心旋转,动态图如下:
1、将当前以当前结点为根结点的子树(between E and S)连接到祖父结点E
2、E和S中较大的结点作为移动到根结点
在这里插入图片描述

左旋前后对比:

图1 左旋前
图2 左旋后

3.右旋

观察左旋之后的树,发现仍然是违反规则的:5、12两个红色的结点连在一起。且不符合变色和左旋的条件。
那么接下来就要使用右旋
右旋条件:
当前父结点是红色,叔结点是黑色,且当前的结点是左子树。
右旋的操作:

1、把父结点变为黑色
2、把祖父结点变为红色
3、以祖父结点旋转
在这里插入图片描述

右旋前后对比;13重新连接。

图1 右旋前
图2 右旋后

3、删除结点需要注意的地方

1、将红黑树当做一棵二叉搜索树,将该结点从二叉搜索树中删除
2、通过旋转和变色操作来修正这棵树,使之重新成为一棵红黑树
关于二叉搜索树的删除结点不是这篇笔记的重点,可以参考我之前的一个刷题笔记,还是比较有用处的:

二叉搜索树的插入、删除、修剪、构造操作(leetcode701、450、669、108)

怕麻烦的话也可以直接看下面的截图:
在这里插入图片描述

2、代码

1、定义结点以及构造函数

每个结点有颜色和值,左右孩子以及父结点指针。
定义带参构造函数

template <class T>
class RBTNode{public:RBTColor color;    // 颜色T key;            // 关键字(键值)RBTNode *left;    // 左孩子RBTNode *right;    // 右孩子RBTNode *parent; // 父结点RBTNode(T value, RBTColor c, RBTNode *p, RBTNode *l, RBTNode *r):key(value),color(c),parent(),left(l),right(r) {}
};

2、定义红黑树类以及声明它的方法

每棵树有一个根结点还有空结点

template <class T>
class RBTree {private:RBTNode<T> *mRoot;    // 根结点public:RBTree();~RBTree();// 前序遍历"红黑树"void preOrder();// 中序遍历"红黑树"void inOrder();// 后序遍历"红黑树"void postOrder();// (递归实现)查找"红黑树"中键值为key的节点RBTNode<T>* search(T key);// (非递归实现)查找"红黑树"中键值为key的节点RBTNode<T>* iterativeSearch(T key);// 查找最小结点:返回最小结点的键值。T minimum();// 查找最大结点:返回最大结点的键值。T maximum();// 找结点(x)的后继结点。即,查找"红黑树中数据值大于该结点"的"最小结点"。RBTNode<T>* successor(RBTNode<T> *x);// 找结点(x)的前驱结点。即,查找"红黑树中数据值小于该结点"的"最大结点"。RBTNode<T>* predecessor(RBTNode<T> *x);// 将结点(key为节点键值)插入到红黑树中void insert(T key);// 删除结点(key为节点键值)void remove(T key);// 销毁红黑树void destroy();// 打印红黑树void print();private:// 前序遍历"红黑树"void preOrder(RBTNode<T>* tree) const;// 中序遍历"红黑树"void inOrder(RBTNode<T>* tree) const;// 后序遍历"红黑树"void postOrder(RBTNode<T>* tree) const;// (递归实现)查找"红黑树x"中键值为key的节点RBTNode<T>* search(RBTNode<T>* x, T key) const;// (非递归实现)查找"红黑树x"中键值为key的节点RBTNode<T>* iterativeSearch(RBTNode<T>* x, T key) const;// 查找最小结点:返回tree为根结点的红黑树的最小结点。RBTNode<T>* minimum(RBTNode<T>* tree);// 查找最大结点:返回tree为根结点的红黑树的最大结点。RBTNode<T>* maximum(RBTNode<T>* tree);// 左旋void leftRotate(RBTNode<T>* &root, RBTNode<T>* x);// 右旋void rightRotate(RBTNode<T>* &root, RBTNode<T>* y);// 插入函数void insert(RBTNode<T>* &root, RBTNode<T>* node);// 插入修正函数void insertFixUp(RBTNode<T>* &root, RBTNode<T>* node);// 删除函数void remove(RBTNode<T>* &root, RBTNode<T> *node);// 删除修正函数void removeFixUp(RBTNode<T>* &root, RBTNode<T> *node, RBTNode<T> *parent);// 销毁红黑树void destroy(RBTNode<T>* &tree);// 打印红黑树void print(RBTNode<T>* tree, T key, int direction);#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)
};

3、左旋

/* * 对红黑树的节点(x)进行左旋转** 左旋示意图(对节点x进行左旋):*      px                              px*     /                               /*    x                               y                *   /  \      --(左旋)-->           / \                #*  lx   y                          x  ry     *     /   \                       /  \*    ly   ry                     lx  ly  ***/
template <class T>
void RBTree<T>::leftRotate(RBTNode<T>* &root, RBTNode<T>* x)
{// 设置x的右孩子为yRBTNode<T> *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->parent == NULL){root = y;            // 如果 “x的父亲” 是空节点,则将y设为根节点}else{if (x->parent->left == x)x->parent->left = y;    // 如果 x是它父节点的左孩子,则将y设为“x的父节点的左孩子”elsex->parent->right = y;    // 如果 x是它父节点的左孩子,则将y设为“x的父节点的左孩子”}// 将 “x” 设为 “y的左孩子”y->left = x;// 将 “x的父节点” 设为 “y”x->parent = y;
}

4、右旋

/* * 对红黑树的节点(y)进行右旋转** 右旋示意图(对节点y进行左旋):*            py                               py*           /                                /*          y                                x                  *         /  \      --(右旋)-->            /  \                     #*        x   ry                           lx   y  *       / \                                   / \                   #*      lx  rx                                rx  ry* */
template <class T>
void RBTree<T>::rightRotate(RBTNode<T>* &root, RBTNode<T>* y)
{// 设置x是当前节点的左孩子。RBTNode<T> *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->parent == NULL) {root = x;            // 如果 “y的父亲” 是空节点,则将x设为根节点}else{if (y == y->parent->right)y->parent->right = x;    // 如果 y是它父节点的右孩子,则将x设为“y的父节点的右孩子”elsey->parent->left = x;    // (y是它父节点的左孩子) 将x设为“x的父节点的左孩子”}// 将 “y” 设为 “x的右孩子”x->right = y;// 将 “y的父节点” 设为 “x”y->parent = x;
}

5、插入操作

内部接口 – insert(root, node)的作用是将"node"节点插入到红黑树中。其中,root是根,node是被插入节点。
外部接口 – insert(key)的作用是将"key"添加到红黑树中。

/* * 将结点插入到红黑树中** 参数说明:*     root 红黑树的根结点*     node 插入的结点        // 对应《算法导论》中的node*/
template <class T>
void RBTree<T>::insert(RBTNode<T>* &root, RBTNode<T>* node)
{RBTNode<T> *y = NULL;RBTNode<T> *x = root;// 1. 将红黑树当作一颗二叉查找树,将节点添加到二叉查找树中。while (x != NULL){y = x;if (node->key < x->key)x = x->left;elsex = x->right;}node->parent = y;if (y!=NULL){if (node->key < y->key)y->left = node;elsey->right = node;}elseroot = node;// 2. 设置节点的颜色为红色node->color = RED;// 3. 将它重新修正为一颗二叉查找树insertFixUp(root, node);
}/* * 将结点(key为节点键值)插入到红黑树中** 参数说明:*     tree 红黑树的根结点*     key 插入结点的键值*/
template <class T>
void RBTree<T>::insert(T key)
{RBTNode<T> *z=NULL;// 如果新建结点失败,则返回。if ((z=new RBTNode<T>(key,BLACK,NULL,NULL,NULL)) == NULL)return ;insert(mRoot, z);
}

6、修正操作

根据条件,分别进行变色、左旋、右旋操作,最后再将根结点置为黑色。
对于叔叔结点是组父结点的左孩子还是右孩子需要分类讨论。
操作的具体步骤,可以返回上文对比看。
insertFixUp(root, node)的作用是对应"上面所讲的第三步"。它是一个内部接口。

/** 红黑树插入修正函数** 在向红黑树中插入节点之后(失去平衡),再调用该函数;* 目的是将它重新塑造成一颗红黑树。** 参数说明:*     root 红黑树的根*     node 插入的结点        // 对应《算法导论》中的z*/
template <class T>
void RBTree<T>::insertFixUp(RBTNode<T>* &root, RBTNode<T>* node)
{RBTNode<T> *parent, *gparent;// 若“父节点存在,并且父节点的颜色是红色”while ((parent = rb_parent(node)) && rb_is_red(parent)){gparent = rb_parent(parent);//若“父节点”是“祖父节点的左孩子”if (parent == gparent->left){RBTNode<T> *uncle = gparent->right;// Case 1条件:叔叔节点是红色,此时采用变色if (uncle && rb_is_red(uncle)){rb_set_black(uncle);rb_set_black(parent);rb_set_red(gparent);node = gparent;continue;}// Case 2条件:叔叔是黑色,且当前节点是右孩子,此时采用左旋if (parent->right == node){RBTNode<T> *tmp;leftRotate(root, parent);tmp = parent;parent = node;node = tmp;}// Case 3条件:叔叔是黑色,且当前节点是左孩子,此时采用右旋rb_set_black(parent);rb_set_red(gparent);rightRotate(root, gparent);} else//若“z的父节点”是“z的祖父节点的右孩子”{RBTNode<T> *uncle = gparent->left;// Case 1条件:叔叔节点是红色if (uncle && rb_is_red(uncle)){rb_set_black(uncle);rb_set_black(parent);rb_set_red(gparent);node = gparent;continue;}// Case 2条件:叔叔是黑色,且当前节点是左孩子if (parent->left == node){RBTNode<T> *tmp;rightRotate(root, parent);tmp = parent;parent = node;node = tmp;}// Case 3条件:叔叔是黑色,且当前节点是右孩子。rb_set_black(parent);rb_set_red(gparent);leftRotate(root, gparent);}}// 将根节点设为黑色rb_set_black(root);
}

7、删除操作

将红黑树内的某一个节点删除。需要执行的操作依次是:首先,将红黑树当作一颗二叉查找树,将该节点从二叉查找树中删除;然后,通过"旋转和重新着色"等一系列来修正该树,使之重新成为一棵红黑树。

3、参考链接

1、红黑树 - C++代码实现
2、leetcode刷题(十):树(红黑树,B树)
3、B站视频,关于红黑树的推导
4、慕课视频,关于模板类的写法指导
5、二叉搜索树的插入、删除、修剪、构造操作(leetcode701、450、669、108)
6、红黑树的删除调整过程(转载)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/377301.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

118 - ZOJ Monthly, July 2012

http://acm.zju.edu.cn/onlinejudge/showContestProblems.do?contestId339 都是赛后做的。。。弱爆了 A题是找由2和5组成的数字的个数 直接打个表就行了 只是比赛的时候不知道怎么打表啊。。 View Code #include<cstdio> #include<cstring> #include<algorith…

edp1.2和edp1.4_EDP​​的完整形式是什么?

edp1.2和edp1.4EDP​​&#xff1a;电子数据处理 (EDP: Electronic Data Processing) EDP is an abbreviation of Electronic Data Processing. It alludes to the functioning of operations of commercial data, documents processing of storing, with the use of a compute…

高效读书心得

1.尽量阅读中文版 虽然有人英文很强&#xff0c;有的翻译很差&#xff0c;但AnyWay 中文阅读与理解的时间&#xff0c;略读与快速定位感兴趣内容的速度还是要快一些。 2.即时批注、总结笔记与交流 虽然爱书&#xff0c;但发现最有效的读书方式还是不断的制造脂批本&…

《MySQL——增删改查以及常用语法》

目录登录和退出MySQL服务器基本语法&#xff08;增删改查&#xff09;登录和退出MySQL服务器 # 登录MySQL 密码 $ mysql -u root -p12345612 # 退出MySQL数据库服务器 exit;基本语法&#xff08;增删改查&#xff09; -- 显示所有数据库 show databases;-- 创建数据库 CREA…

WCF简介

一、简介 WCF是Windows Communication Foundation缩写&#xff0c;是Microsoft为构建面向服务的应用提供的分布式通信编程框架&#xff0c;是.NET Framework 3.5的重要组成部分。使用该框架&#xff0c;开发人员可以构建跨平台、安全、可靠和支持事务处理的企业级互联应用解决方…

css链接样式_CSS中的样式链接

css链接样式CSS样式链接 (CSS Styling Links) The links in CSS can be styled in various ways to make our website more presentable and attractive. The links can also be styled depending on their states e.g. visited, active, hover, etc. CSS中的链接可以通过各种方…

《MySQL——约束》

目录主键约束唯一主键非空约束默认约束外键约束主键约束 -- 主键约束 -- 使某个字段不重复且不得为空&#xff0c;确保表内所有数据的唯一性。 CREATE TABLE user (id INT PRIMARY KEY,name VARCHAR(20) );-- 联合主键 -- 联合主键中的每个字段都不能为空&#xff0c;并且加起…

UIControl事件

CHENYILONG BlogUIControl事件 FullscreenUIControl事件1.UIControlEventTouchDown单点触摸按下事件&#xff1a;用户点触屏幕&#xff0c;或者又有新手指落下的时候。2.UIControlEventTouchDownRepeat多点触摸按下事件&#xff0c;点触计数大于1&#xff1a;用户按下第二、三、…

C++ 为什么要使用#ifdef __cplusplus extern C { #endif

经常看到别人的头文件 有这样的代码 #ifdef __cplusplus extern "C" { #endif// C 样式 的函数#ifdef __cplusplus } #endif 为什么要这样呢&#xff1f; 因为 C 语言不支持重载函数 也就是同名函数&#xff0c;参数却不一样,C支持&#xff0c;其编译器对函数名的处理…

css中的媒体查询_CSS中的媒体查询

css中的媒体查询CSS | 媒体查询 (CSS | Media Queries) Creating a web page is not an easy task as it requires loads of content and data so that it becomes strongly responsive to the users. To do that various contents are even added e.g.: resources, informativ…

SharePoint2013安装组件时AppFabric时出现1603错误,解决方法:

采用PowerShell命令批量下载必备组件: 下载完成后&#xff0c;采用批处理命令安装必备组件。 注&#xff1a;SPS2013安装必备组件及批处理下载地址&#xff1a; 需要将必备组件放在安装文件的PrerequisiteInstallerFiles文件夹中&#xff0c;将PreReq2013.bat放在安装文件根目录…

《MySQL——数据表设计三大范式》

目录数据表设计范式第一范式第二范式第三范式数据表设计范式 第一范式 数据表中的所有字段都是不可分割的原子值。 字段值还可以继续拆分的&#xff0c;就不满足第一范式&#xff0c;如下&#xff1a; 下面这个&#xff0c;更加贴合第一范式&#xff1a; 范式设计得越详细&…

三道简单树型dp+01背包~~hdu1561,poj1947,zoj3626

以前学树型dp就是随便的看了几道题&#xff0c;没有特别注意树型dp中的小分类的总结&#xff0c;直到上次浙大月赛一道很简单的树型dp都不会&#xff0c;才意识到自己太水了&#xff5e;&#xff5e;come on&#xff01; hdu1561&#xff1a;题目给出了很多棵有根树&#xff0c…

css 字体图标更改颜色_在CSS中更改字体

css 字体图标更改颜色CSS字体属性 (CSS font properties ) Font properties in CSS is used to define the font family, boldness, size, and the style of a text. CSS中的字体属性用于定义字体系列 &#xff0c; 粗体 &#xff0c; 大小和文本样式 。 Syntax: 句法&#xf…

深入new/delete:Operator new的全局重载

Operator new 的全局重载 原文地址&#xff1a;http://blog.csdn.net/zhenjing/article/details/4354880 我们经常看到这么一句话&#xff1a; operator new 可以重载&#xff0c; placement new 不可重载。其实此处所说的不可重载应该是指全局的 placement new 不可重载&#…

C++基础知识点整理

基本语法 1、static关键字的作用 1、全局静态变量 加了static关键字的全局变量只能在本文件中使用。 存储在静态存储区&#xff0c;整个程序运行期间都存在。 2、局部静态变量 作用域仍为局部作用域。 不过离开作用域之后&#xff0c;并没有销毁&#xff0c;而是贮存程序中&a…

Haskell学习笔记

《learn you a Haskell》这书的结构与常见的语言入门教材完全不一样。事实上&#xff0c;即使学到第八章&#xff0c;你还是写不出正常的程序…因为到现在为止还没告诉你入口点模块怎么写&#xff0c;IO部分也留在了最后几章才介绍。最重要的是&#xff0c;没有系统的总结数据类…

组合问题 已知组合数_组合和问题

组合问题 已知组合数Description: 描述&#xff1a; This is a standard interview problem to make some combination of the numbers whose sum equals to a given number using backtracking. 这是一个标准的面试问题&#xff0c;它使用回溯功能将总和等于给定数字的数字进…

可变参数模板、右值引用带来的移动语义完美转发、lambda表达式的理解

可变参数模板 可变参数模板对参数进行了高度泛化&#xff0c;可以表示任意数目、任意类型的参数&#xff1a; 语法为&#xff1a;在class或者typename后面带上省略号。 Template<class ... T> void func(T ... args) {// }T:模板参数包&#xff0c;args叫做函数参数包 …

BI-SqlServer

一.概述 SqlServer数据仓库ETL组件 IntegrationServiceOLAP组件 AnalysisService报表 ReportingServiceMDX(查多维数据集用的)和DMX(查挖掘模型用的)。二.商业智能-Analysis Services 项目 构建挖掘模型1构建挖掘模型2构建挖掘模型3三.商业智能-SqlServerAnalysis-Asp.net WebS…