RedBlack-Tree(红黑树)原理及C++代码实现
众所周知,红黑树是用途很广的平衡二叉搜索树,用过的都说好。所以我们来看看红黑树的是怎么实现的吧。
红黑树顾名思义,通过红与黑两种颜色来给每个节点上色。其中根结点和叶子结点一定是黑色的,并且红色结点的两个孩子一定是黑色的,每个结点到所有后代叶子的简单路径上,均包含相同数目的黑色结点(黑高bh)。
这里和其他树有些区别的是,红黑树的叶子结点并不包含关键字,它只是用来维持红黑树的性质。所以包含关键字的结点都是内部结点。
这样的性质也带来了很多有意思的现象。下面我们忽略所谓的叶子结点来看看。
1.红色结点的孩子要么没有,要么是两个黑色结点,不会有单个孩子结点的时候。
2.如果某个结点只有一个孩子结点,那么那个孩子结点一定是红色的。
3.用吸收的角度去看,即黑色结点可以吸收它的红色孩子结点,红黑树就像一个2-3-4树。
下面我们来看看代码吧,代码用了一个小技巧,即所有的叶子结点都用一个nil结点来表示,指向nullptr的指针现在全部指向nil。
代码如下:(仅供参考)
#include <iostream>
using namespace std; class RBT {
private :
enum {RED = , BLACK};
struct Node {
int key;
bool color;
Node * left;
Node * right;
Node * parent;
Node(int k = , bool c = BLACK, Node *l = nullptr, Node *r = nullptr, Node *p = nullptr)
: key(k), color(c), left(l), right(r), parent(p) {}
};
private :
Node * nil;
Node * root;
private :
void leftRotate(Node * x);
void rightRotate(Node * x);
void fixup_insert(Node * p);
void fixup_remove(Node * p);
void transplant(Node * old_t, Node * new_t);
void insert(Node * p);
void remove(Node * p);
Node * search(Node * p, const int k);
Node * minimum(Node * p);
Node * maximum(Node * p);
void inorderWalk(Node * p) const;
public :
RBT() : nil(new Node), root(nil) {}
~RBT() {delete nil;}
void insert(const int key) {insert(new Node(key, RED, nil, nil, nil));}
void remove(const int key) {remove(search(root, key));}
bool search(const int key) {return (search(root, key) == nil ? false : true);}
int minimum() {return minimum(root)->key;}
int maximum() {return maximum(root)->key;}
int predecessor(const int key);
int successor(const int key); friend ostream &operator<<(ostream &os, const RBT &t);
}; void RBT::inorderWalk(Node * p) const {
if (p != nil) {
inorderWalk(p->left);
cout << p->key << ' ';
inorderWalk(p->right);
}
} RBT::Node * RBT::search(Node * p, const int k) {
if (p == nil || k == p->key)
return p;
if (k < p->key)
return search(p->left, k);
else
return search(p->right, k);
} RBT::Node * RBT::minimum(Node * p) {
if (p == nil)
return p;
while (p->left != nil)
p = p->left;
return p;
} RBT::Node * RBT::maximum(Node * p) {
if (p == nil)
return p;
while (p->right != nil)
p = p->right;
return p;
} int RBT::predecessor(const int k) {
Node * p = search(root, k);
if (p == nil)
return ;
if (p->left != nil)
return maximum(p->left)->key;
Node * y = p->parent;
while (y != nil && y->left == p) {
p = y;
y = y->parent;
}
return y->key;
} int RBT::successor(const int k) {
Node * p = search(root, k);
if (p == nil)
return ;
if (p->right != nil)
return minimum(p->right)->key;
Node * y = p->parent;
while (y != nil && y->right == p) {
p = y;
y = y->parent;
}
return y->key;
} void RBT::leftRotate(Node * x) { //assume:x->right != nil
Node * y = x->right;
x->right = y->left;
if (y->left != nil)
y->left->parent = x;
y->parent = x->parent;
if (x->parent == nil)
root = y;
else if (x == x->parent->left)
x->parent->left = y;
else
x->parent->right = y;
y->left = x;
x->parent = y;
} void RBT::rightRotate(Node * x) { //assume:x->left != nil
Node * y = x->left;
x->left = y->right;
if (y->right != nil)
y->right->parent = x;
y->parent = x->parent;
if (x->parent == nil)
root = y;
else if (x == x->parent->right)
x->parent->right = y;
else
x->parent->left = y;
y->right = x;
x->parent = y;
} void RBT::insert(Node * p) {
if (p == nullptr)
return ;
Node * x = root;
Node * y = nil;
while (x != nil) {
y = x;
if (x->key < p->key)
x = x->right;
else
x = x->left;
}
p->parent = y;
if (y == nil)
root = p;
else if (y->key < p->key)
y->right = p;
else
y->left = p;
fixup_insert(p);
} void RBT::fixup_insert(Node * p) {
while (p->parent->color == RED) {
if (p->parent == p->parent->parent->left) {
Node * y = p->parent->parent->right;
if (y->color == RED) { //case 1
p->parent->color = BLACK;
y->color = BLACK;
p->parent->parent->color = RED;
p = p->parent->parent;
}
else {
if (p == p->parent->right) { //case 2
p = p->parent;
leftRotate(p);
}
p->parent->color = BLACK; //case 3
p->parent->parent->color = RED;
rightRotate(p->parent->parent);
}
}
else { //with "right" and "left" exchanged
Node * y = p->parent->parent->left;
if (y->color == RED) { //case 1
p->parent->color = BLACK;
y->color = BLACK;
p->parent->parent->color = RED;
p = p->parent->parent;
}
else {
if (p == p->parent->left) { //case 2
p = p->parent;
rightRotate(p);
}
p->parent->color = BLACK; //case 3
p->parent->parent->color = RED;
leftRotate(p->parent->parent);
}
}
}
root->color = BLACK;
} void RBT::transplant(Node * old_t, Node * new_t) {
if (old_t->parent == nil)
root = new_t;
else if (old_t == old_t->parent->left)
old_t->parent->left = new_t;
else
old_t->parent->right = new_t;
new_t->parent = old_t->parent;
} void RBT::fixup_remove(Node * x) {
Node * z = nil;
while (x != root && x->color == BLACK) {
if (x == x->parent->left) {
z = x->parent->right;
/*case 1*/ if (z->color == RED) {
z->color = BLACK;
x->parent->color = RED;
leftRotate(x->parent);
z = x->parent->right; //new z
}
/*case 2*/ if (z->left->color == BLACK && z->right->color == BLACK) {
z->color = RED;
x = x->parent;
}
else {
/*case 3*/ if (z->right->color == BLACK) {
z->left->color = BLACK;
z->color = RED;
rightRotate(z);
z = x->parent->right;
}
/*case 4*/ z->color = x->parent->color;
x->parent->color = BLACK;
z->right->color = BLACK;
leftRotate(x->parent);
x = root; //exit while
}
}
else {
z = x->parent->left;
/*case 1*/ if (z->color == RED) {
z->color = BLACK;
x->parent->color = RED;
rightRotate(x->parent);
z = x->parent->left; //new z
}
/*case 2*/ if (z->right->color == BLACK && z->left->color == BLACK) {
z->color = RED;
x = x->parent;
}
else {
/*case 3*/ if (z->left->color == BLACK) {
z->right->color = BLACK;
z->color = RED;
leftRotate(z);
z = x->parent->left;
}
/*case 4*/ z->color = x->parent->color;
x->parent->color = BLACK;
z->left->color = BLACK;
rightRotate(x->parent);
x = root; //exit while
}
}
}
x->color = BLACK;
} void RBT::remove(Node * p) {
if (p == nil)
return ;
Node * y = p;
Node * x = nil;
bool y_originalColor = y->color;
if (p->left == nil) {
x = p->right;
transplant(p, p->right);
}
else if (p->right == nil) {
x = p->left;
transplant(p, p->left);
}
else {
y = minimum(p->right);
y_originalColor = y->color;
x = y->right;
if (y->parent == p)
x->parent = y; //maybe x == nil
else {
transplant(y, y->right);
y->right = p->right;
y->right->parent = y;
}
transplant(p, y);
y->left = p->left;
y->left->parent = y;
y->color = p->color;
}
delete p;
if (y_originalColor == BLACK)
fixup_remove(x);
} ostream &operator<<(ostream &os, const RBT &t) {
t.inorderWalk(t.root);
return os;
}
RedBlack-Tree(红黑树)原理及C++代码实现的更多相关文章
- 红黑树原理详解及golang实现
目录 红黑树原理详解及golang实现 二叉查找树 性质 红黑树 性质 operation 红黑树的插入 golang实现 类型定义 leftRotate RightRotate Item Inter ...
- Java面试题之红黑树原理
红黑树原理: 每个节点都只能是红色或黑色的: 根节点是黑色的: 每个叶节点(空节点)是黑色的: 如果一个节点是红色的,那么他的子节点都是黑色的: 从任意一个节点到其每个子节点的路径都有相同数目的黑色节 ...
- Red Black Tree 红黑树 AVL trees 2-3 trees 2-3-4 trees B-trees Red-black trees Balanced search tree 平衡搜索树
小结: 1.红黑树:典型的用途是实现关联数组 2.旋转 当我们在对红黑树进行插入和删除等操作时,对树做了修改,那么可能会违背红黑树的性质.为了保持红黑树的性质,我们可以通过对树进行旋转,即修改树中某些 ...
- 从二叉查找树到平衡树:avl, 2-3树,左倾红黑树(含实现代码),传统红黑树
参考:自平衡二叉查找树 ,红黑树, 算法:理解红黑树 (英文pdf:红黑树) 目录 自平衡二叉树介绍 avl树 2-3树 LLRBT(Left-leaning red-black tree左倾红黑树 ...
- HashMap1.7和1.8,红黑树原理!
jdk 1.7 概述 HashMap基于Map接口实现,元素以键值对的方式存储,并允许使用null键和null值,但只能有一个键作为null,因为key不允许重复,另外HashMap不能保证放入元素的 ...
- 排序二叉树、平衡二叉树、红黑树、B+树
一.排序二叉树(Binary Sort Tree,BST树) 二叉排序树,又叫二叉搜索树.有序二叉树(ordered binary tree)或排序二叉树(sorted binary tree). 1 ...
- AVL树、splay树(伸展树)和红黑树比较
AVL树.splay树(伸展树)和红黑树比较 一.AVL树: 优点:查找.插入和删除,最坏复杂度均为O(logN).实现操作简单 如过是随机插入或者删除,其理论上可以得到O(logN)的复杂度,但是实 ...
- 第十四章 红黑树——C++代码实现
红黑树的介绍 红黑树(Red-Black Tree,简称R-B Tree),它一种特殊的二叉查找树.红黑树是特殊的二叉查找树,意味着它满足二叉查找树的特征:任意一个节点所包含的键值,大于等于左孩子的键 ...
- 红黑树 - C++代码实现
红黑树的介绍 红黑树(Red-Black Tree,简称R-B Tree),它一种特殊的二叉查找树.红黑树是特殊的二叉查找树,意味着它满足二叉查找树的特征:任意一个节点所包含的键值,大于等于左孩子的键 ...
- 红黑树插入与删除完整代码(dart语言实现)
之前分析了红黑树的删除,这里附上红黑树的完整版代码,包括查找.插入.删除等.删除后修复实现了两种算法,均比之前的更为简洁.一种是我自己的实现,代码非常简洁,行数更少:一种是Linux.Java等源码版 ...
随机推荐
- Python 学习基础
一.编程语言的发展历史 按照翻译方式分为两类: # 编译型(类似于谷歌翻译):如c.java, 运行速度快,调试麻烦 # 解释型(l类似于同声传译):如Python,运行速度慢,调试麻烦 按照特点总结 ...
- Maven - web 实例
版权所有,未经授权,禁止转载 章节 Maven – 简介 Maven – 工作原理 Maven – Repository(存储库) Maven – pom.xml 文件 Maven – 依赖管理 Ma ...
- vue的开发中v-for报错 [vue/require-v-for-key] Elements in iteration expect to have 'v-bind:key' directives.
用的VS Code 工具,安装了vetur插件,报错了如下 [eslint-plugin-vue] [vue/require-v-for-key] Elements in iteration expe ...
- kube-controller-manager配置详解
KUBE_MASTER="--master=http://10.83.52.137:8080" KUBE_CONTROLLER_MANAGER_ARGS=" "
- pytorch 数据操作
数据操作 在深度学习中,我们通常会频繁地对数据进行操作.作为动手学深度学习的基础,本节将介绍如何对内存中的数据进行操作. 在PyTorch中,torch.Tensor是存储和变换数据的主要工具.如果你 ...
- svg用例
圆<circle cx="x" cy="y" r="r" style="stroke:black;fill:none&quo ...
- dfs--迷宫
题目背景 给定一个N*M方格的迷宫,迷宫里有T处障碍,障碍处不可通过.给定起点坐标和终点坐标,问: 每个方格最多经过1次,有多少种从起点坐标到终点坐标的方案.在迷宫中移动有上下左右四种方式,每次只能移 ...
- UVA 10269 Super Mario,最短路+动态规划
这个题目我昨晚看到的,没什么思路,因为马里奥有boot加速器,只要中间没有城堡,即可不耗时间和脚力,瞬间移动不超过L距离,遇见城堡就要停下来,当然不能该使用超过K次...我纠结了很久,最终觉得还是只能 ...
- MySQL-慢日志slow log
文件结构 1.目录结构 drwxrwxr-x mysql mysql Mar bin drwxrwxr-x mysql mysql Dec : binlogdir -rw-r--r-- mysql m ...
- 【MySQL参数】- query_cache_type
MySQL为什么要关闭查询缓存 https://blog.csdn.net/liqfyiyi/article/details/50178591 Query cache的优化方法 https://blo ...