大名鼎鼎的红黑树,你get了么?2-3树 绝对平衡 右旋转 左旋转 颜色反转
前言
11.1新的一月加油!这个购物狂欢的季节,一看,已囊中羞涩!赶紧来恶补一下红黑树和2-3树吧!红黑树真的算是大名鼎鼎了吧?即使你不了解它,但一定听过吧?下面跟随我来揭开神秘的面纱吧!
一、2-3树
1、抢了红黑树的光环?
今天的主角是红黑树,是无疑的,主角光环在呢!那2-3树又是什么鬼呢?学习2-3树不仅对理解红黑树有帮助,对理解B类树,也是有巨大帮助的,所以学习2-3树很必要!
2、基本性质
2-3树满足二分搜索树的基本性质,但节点可以存放一个元素或两个元素!如下图,就是2-3树:
说明:2-3树一颗绝对平衡的树(绝对平衡:对于任意一个节点,左右子树高度相同)
3、维护绝对平衡
2-3树在插入过程中如何维护绝对平衡呢?进行画图演示,实在有点不好画,如下图:
说明:
1、不能将新节点插入到空节点
因为那样如上图,就不满足绝对平衡了,所以可以将37和42合并,2-3支持3节点。
2、不支持4节点,进行拆分
再插入12时,也不能插入空节点,也要合并,但2-3树不支持4节点,所以进行进行拆分。
3、子节点达到3节点,合并到父节点
再依次插入18、6,达到4节点,进行拆分,但不符合绝对平衡了怎么办?将12和37合并,就形成了最后3节点的图了
总结:讲到这里,应该对2-3树如何维护绝对平衡,应该了解了吧?理解2-3树,对于再理解红黑树,是非常有帮助的,其实,它们有等价性的,接下来会说明的。
二、红黑树
1、红黑树和2-3树的等价性
也想达到像2-3树那样的绝对平衡,但2-3树的实现比较麻烦,所以产生了红黑树;那么,红黑树和2-3树有怎么样的等价性呢?如下图:
说明:红黑树最开始想用红线区别b、c,但实现起来比较困难,然后用红黑来表示节点,就比较好实现了!
红黑树和2-3树总体对比图,可以参考一下:
2、红黑树5个重要性质
1、引自《算法导论》
红黑树有五个重要性质,引自算法界一本圣洁《算法导论》中的内容,如下:
是不是看着有点晕,下面我进行解释。
2、5个重要性质
1、每一个节点或者红色的,或是黑色的
2、根节点是黑色的
3、每一个叶子节点(最后的空节点)是黑色的
4、如果一个节点是红色的,那么它的孩子节点都是黑色的
5、从任意节点到叶子节点,经过的黑色节点是一样的
解释:最重要的性质是第五条,前4条在理解2-3树之后,就很好理解了,第5条性质说明了:红黑树是保持“黑平衡”的二叉树;
严格意义上来说,红黑树不是平衡二叉树,最大高度:2logn,但是时间复杂度仍然是O(logn),因为2是常数,但比AVL树查询要稍微慢一些。
三、红黑树添加元素
红黑树添加元素,比较繁琐,因为要保持上面的五个性质,要不然就不是红黑树了;
1、保持根节点为节点
红黑树的节点类也可以从二分搜索树上进行修改,但要新增“color”成员变量,来标注节点颜色,节点类如下:
template<typename Key, typename Value>
class RBTree {
private:
static const bool RED = true;
static const bool BLACK = false; struct Node {
Key key;
Value value;
Node *left;
Node *right;
bool color; Node(Key key, Value value) {
this->key = key;
this->value = value;
this->left = this->right = nullptr;
color = RED; //默认初始化为红色
} Node(Node *node) {
this->key = node->key;
this->value = node->value;
this->left = node->left;
this->right = node->right;
this->color = node->color;
}
}; Node *root;
int size;
}
因为红黑树性质1要求根节点为黑色,所以要保持根节点为黑色;
2、左旋转
像AVL树一样,红黑树也需要左旋和右旋,如下图就需要左旋转,因为“红色节点是左倾斜的”:
说明:图中黑色字体标识黑色节点,红色表示红色节点,并演示了旋转过程,最后还要改变节点颜色。
3、左旋转代码实现
代码如下:
Node *leftRotate(Node *node) {
Node *x = node->right;
node->right = x->left;
x->left = node; x->color = node->color;
node->color = RED; return x;
}
4、颜色反转
下面这种情况就需要颜色反转,如下图:
5、颜色反转代码实现
代码如下:
void flipColors(Node *node) {
node->color = RED;
node->left->color = BLACK;
node->right->color = BLACK;
}
6、右旋转
下面情况需要右旋转,如下图:
旋转之后,如下图:
7、右旋转代码如下
代码如下:
Node *rightRotate(Node *node) {
Node *x = node->left;
node->left = x->right;
x->right = node; x->color = node->color;
node->color = RED; return x;
}
8、总体流程图
9、总体代码
总体代码如下,供参考和学习:
#ifndef RED_BLACK_TREE_RBTREE_H
#define RED_BLACK_TREE_RBTREE_H #include <iostream>
#include <vector> template<typename Key, typename Value>
class RBTree {
private:
static const bool RED = true;
static const bool BLACK = false; struct Node {
Key key;
Value value;
Node *left;
Node *right;
bool color; Node(Key key, Value value) {
this->key = key;
this->value = value;
this->left = this->right = nullptr;
color = RED;
} Node(Node *node) {
this->key = node->key;
this->value = node->value;
this->left = node->left;
this->right = node->right;
this->color = node->color;
}
}; Node *root;
int size; public: RBTree() {
root = nullptr;
size = ;
} ~RBTree() {
destroy(root);
} int getSize() {
return size;
} int isEmpty() {
return size == ;
} bool isRed(Node *node) {
if (node == nullptr) {
return BLACK;
}
return node->color;
} void add(Key key, Value value) {
root = add(root, key, value);
root->color = BLACK;
} bool contains(Key key) {
return getNode(root, key) != nullptr;
} Value *get(Key key) {
Node *node = getNode(root, key);
return node == nullptr ? nullptr : &(node->value);
} void set(Key key, Value newValue) {
Node *node = getNode(root, key);
if (node != nullptr) {
node->value = newValue;
}
} private: // 向以node为根的二叉搜索树中,插入节点(key, value)
// 返回插入新节点后的二叉搜索树的根
Node *add(Node *node, Key key, Value value) {
if (node == nullptr) {
size++;
return new Node(key, value);
}
if (key == node->key) {
node->value = value;
} else if (key < node->key) {
node->left = add(node->left, key, value);
} else {
node->right = add(node->right, key, value);
} if (isRed(node->right) && !isRed(node->left)) {
node = leftRotate(node);
} if (isRed(node->left) && isRed(node->left->left)) {
node = rightRotate(node);
} if (isRed(node->left) && isRed(node->right)) {
flipColors(node);
}
return node;
} // 在以node为根的二叉搜索树中查找key所对应的Node
Node *getNode(Node *node, Key key) {
if (node == nullptr) {
return nullptr;
}
if (key == node->key) {
return node;
} else if (key < node->key) {
return getNode(node->left, key);
} else {
return getNode(node->right, key);
}
} void destroy(Node *node) {
if (node != nullptr) {
destroy(node->left);
destroy(node->right);
delete node;
size--;
}
} Node *leftRotate(Node *node) {
Node *x = node->right;
node->right = x->left;
x->left = node; x->color = node->color;
node->color = RED; return x;
} Node *rightRotate(Node *node) {
Node *x = node->left;
node->left = x->right;
x->right = node; x->color = node->color;
node->color = RED; return x;
} void flipColors(Node *node) {
node->color = RED;
node->left->color = BLACK;
node->right->color = BLACK;
}
}; #endif //RED_BLACK_TREE_RBTREE_H
总结
面试时99.9%不会让手写一下红黑树的添加过程,除非你面试算法工程师,那就打扰了!主要理解红黑树的性质、左旋和右旋等。
欢迎点赞和评论,感谢支持!
大名鼎鼎的红黑树,你get了么?2-3树 绝对平衡 右旋转 左旋转 颜色反转的更多相关文章
- TreeMap----的实现原理(红黑树)
TreeMap的实现是红黑树算法的实现,所以要了解TreeMap就必须对红黑树有一定的了解,其实这篇博文的名字叫做:根据红黑树的算法来分析TreeMap的实现,但是为了与Java提高篇系列博文保持一致 ...
- 论AVL树与红黑树
首先讲解一下AVL树: 例如,我们要输入这样一串数字,10,9,8,7,15,20这样一串数字来建立AVL树 1,首先输入10,得到一个根结点10 2,然后输入9, 得到10这个根结点一个左孩子结点9 ...
- 红黑树(Red-Black tree)
红黑树又称红-黑二叉树,它首先是一颗二叉树,它具体二叉树所有的特性.同时红黑树更是一颗自平衡的排序二叉树.我们知道一颗基本的二叉树他们都需要满足一个基本性质–即树中的任何节点的值大于它的左子节点,且小 ...
- Atitit 常见的树形结构 红黑树 二叉树 B树 B+树 Trie树 attilax理解与总结
Atitit 常见的树形结构 红黑树 二叉树 B树 B+树 Trie树 attilax理解与总结 1.1. 树形结构-- 一对多的关系1 1.2. 树的相关术语: 1 1.3. 常见的树形结构 ...
- 通过分析 JDK 源代码研究 TreeMap 红黑树算法实现
本文转载自http://www.ibm.com/developerworks/cn/java/j-lo-tree/ 目录: TreeSet 和 TreeMap 的关系 TreeMap 的添加节点 Tr ...
- Sedgewick的红黑树
红黑树一直是数据结构中的难点,大部分关于算法与数据结构的学习资料(包括<算法导论>)对于这部分的讲解都是上来就下定义,告诉我们红黑树这个性质那个性质,插入删除要注意1234点,但是基本没有 ...
- 红黑树(二)之 C语言的实现
概要 红黑树在日常的使用中比较常用,例如Java的TreeMap和TreeSet,C++的STL,以及Linux内核中都有用到.之前写过一篇文章专门介绍红黑树的理论知识,本文将给出红黑数的C语言的实现 ...
- 红黑树(四)之 C++的实现
概要 前面分别介绍红黑树的理论知识和红黑树的C语言实现.本章是红黑树的C++实现,若读者对红黑树的理论知识不熟悉,建立先学习红黑树的理论知识,再来学习本章. 目录1. 红黑树的介绍2. 红黑树的C++ ...
- 红黑树(五)之 Java的实现
概要 前面分别介绍红黑树的理论知识.红黑树的C语言和C++的实现.本章介绍红黑树的Java实现,若读者对红黑树的理论知识不熟悉,建立先学习红黑树的理论知识,再来学习本章.还是那句老话,红黑树的C/C+ ...
随机推荐
- 在本地没有安装Oracle的情况下,使用plsql远程连接数据库
远程连接数据库的方式不止一种.这里主要写在本地没有安装Oracle的情况下,使用plsql可视化工具远程连接数据库的方式. 一.前提 首先你本地得安装有plsql工具.具体的安装步骤就不多说了. 二. ...
- java.lang.NoSuchMethodError: org.springframework.boot.builder.SpringApplicationBuilder.<init>([Ljava
搭建spring cloud的时候,报以下错误: java.lang.NoSuchMethodError: org.springframework.boot.builder.SpringApplica ...
- 基于Orangpi Zero和Linux ALSA实现WIFI无线音箱(三)
作品已经完成,先上源码: https://files.cnblogs.com/files/qzrzq1/WIFISpeaker.zip 全文包含三篇,这是第三篇,主要讲述接收端程序的原理和过程. 第一 ...
- 哎呀,我老大写Bug啦——记一次MessageQueue的优化
MessageQueue,顾名思义消息队列,在系统开发中也是用的比较多的一个中间件吧.我们这里主要用它来做日志管理和订单管理的,记得老老大(恩,是的,就是老老大,因为他已经跳槽了)还在的时候,当时也是 ...
- SpringBoot进阶教程(三十)整合Redis之Sentinel哨兵模式
Redis-Sentinel是官方推荐的高可用解决方案,当redis在做master-slave的高可用方案时,假如master宕机了,redis本身(以及其很多客户端)都没有实现自动进行主备切换,而 ...
- Gradle入门到实战(一) — 全面了解Gradle
声明:本文来自汪磊的博客,转载请注明出处 可关注个人公众号,那里更新更及时,阅读体验更好: 友情提示由于文章是从个人公众号拷贝过来整理的,发现图片没有正常显示,没关注公众号的同学可通过如下链接查看: ...
- JAVA SPI(Service Provider Interface)原理、设计及源码解析(其一)
背景 团队内部轮流技术分享,其他人都是分享源码,我每次都是设计和架构,感觉自己太特立独行.这次我要合群点,分享点源码. 概念 Service Provider Interface:服务提供方接口.是一 ...
- RazorPage 小测试,作下记录
@page@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Xsrf@{}@functions{ public AppInfo item { ...
- C#-Xamarin的Android项目开发(一)——创建项目
创建项目 使用Xamarin开发安卓项目,首先需要安装VS2017以上版本.因为VS2017以上的版本,可以直接创建Xamarin项目. 另外用Xamarin开发安卓项目,还需要使用Intel的CPU ...
- js实现在当前页面搜索高亮显示字的方法
在html页面上,有时候会遇到一些检索高亮显示的问题,具体用js是实现的方式,代码展示. Jsp页面设置方式 <li class="pull-left" id="s ...