众所周知,红黑树是用途很广的平衡二叉搜索树,用过的都说好。所以我们来看看红黑树的是怎么实现的吧。

红黑树顾名思义,通过红与黑两种颜色来给每个节点上色。其中根结点和叶子结点一定是黑色的,并且红色结点的两个孩子一定是黑色的,每个结点到所有后代叶子的简单路径上,均包含相同数目的黑色结点(黑高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++代码实现的更多相关文章

  1. 红黑树原理详解及golang实现

    目录 红黑树原理详解及golang实现 二叉查找树 性质 红黑树 性质 operation 红黑树的插入 golang实现 类型定义 leftRotate RightRotate Item Inter ...

  2. Java面试题之红黑树原理

    红黑树原理: 每个节点都只能是红色或黑色的: 根节点是黑色的: 每个叶节点(空节点)是黑色的: 如果一个节点是红色的,那么他的子节点都是黑色的: 从任意一个节点到其每个子节点的路径都有相同数目的黑色节 ...

  3. Red Black Tree 红黑树 AVL trees 2-3 trees 2-3-4 trees B-trees Red-black trees Balanced search tree 平衡搜索树

    小结: 1.红黑树:典型的用途是实现关联数组 2.旋转 当我们在对红黑树进行插入和删除等操作时,对树做了修改,那么可能会违背红黑树的性质.为了保持红黑树的性质,我们可以通过对树进行旋转,即修改树中某些 ...

  4. 从二叉查找树到平衡树:avl, 2-3树,左倾红黑树(含实现代码),传统红黑树

    参考:自平衡二叉查找树 ,红黑树, 算法:理解红黑树 (英文pdf:红黑树) 目录 自平衡二叉树介绍 avl树 2-3树 LLRBT(Left-leaning red-black tree左倾红黑树 ...

  5. HashMap1.7和1.8,红黑树原理!

    jdk 1.7 概述 HashMap基于Map接口实现,元素以键值对的方式存储,并允许使用null键和null值,但只能有一个键作为null,因为key不允许重复,另外HashMap不能保证放入元素的 ...

  6. 排序二叉树、平衡二叉树、红黑树、B+树

    一.排序二叉树(Binary Sort Tree,BST树) 二叉排序树,又叫二叉搜索树.有序二叉树(ordered binary tree)或排序二叉树(sorted binary tree). 1 ...

  7. AVL树、splay树(伸展树)和红黑树比较

    AVL树.splay树(伸展树)和红黑树比较 一.AVL树: 优点:查找.插入和删除,最坏复杂度均为O(logN).实现操作简单 如过是随机插入或者删除,其理论上可以得到O(logN)的复杂度,但是实 ...

  8. 第十四章 红黑树——C++代码实现

    红黑树的介绍 红黑树(Red-Black Tree,简称R-B Tree),它一种特殊的二叉查找树.红黑树是特殊的二叉查找树,意味着它满足二叉查找树的特征:任意一个节点所包含的键值,大于等于左孩子的键 ...

  9. 红黑树 - C++代码实现

    红黑树的介绍 红黑树(Red-Black Tree,简称R-B Tree),它一种特殊的二叉查找树.红黑树是特殊的二叉查找树,意味着它满足二叉查找树的特征:任意一个节点所包含的键值,大于等于左孩子的键 ...

  10. 红黑树插入与删除完整代码(dart语言实现)

    之前分析了红黑树的删除,这里附上红黑树的完整版代码,包括查找.插入.删除等.删除后修复实现了两种算法,均比之前的更为简洁.一种是我自己的实现,代码非常简洁,行数更少:一种是Linux.Java等源码版 ...

随机推荐

  1. C# 互操作性入门系列(二):使用平台调用调用Win32 函数

    好文章搬用工模式启动ing ..... { 文章中已经包含了原文链接 就不再次粘贴了 言明 改文章是一个系列,但只收录了2篇,原因是 够用了 } --------------------------- ...

  2. 寒假day20

    今天解决了部分信息爬取不下来的问题

  3. js获取浏览器窗口大小

    摘抄:https://blog.csdn.net/qq_27628085/article/details/81947478 常用: JS 获取浏览器窗口大小       // 获取窗口宽度   if ...

  4. oracle (6)---SQL 数据关联查询

    SQL 数据关联查询 Structure Query Language 从多(n)张表查询对应记录信息,必须有至少n-1个关联条件,否则会出现笛卡尔积的情况.1. 等值连接:没有连接关系的数据不会被查 ...

  5. k8常用操作

    1.当delete pod失败时,使用下面命令强制删除Terminging状态下的pod kubectl delete pod xxxxxx --grace-period=0 --force 2.

  6. Maven:org.apache.maven.archiver.MavenArchiver.getManifest错误

    Eclipse导入maven多模块工程时报错org.apache.maven.archiver.MavenArchiver.getManifestorg.apache.maven.archiver.M ...

  7. Python笔记_第五篇_Python数据分析基础教程_相关安装和版本查看

    1. IDE说明: 所有的案例用Anacoda中的Jupiter工具进行交互式讲解. 2. 版本和安装: NumPy从如下网站安装:http://sourceforge.net/projects/nu ...

  8. 吴裕雄--天生自然C++语言学习笔记:C++简介

    C++ 是一种中级语言,它是由 Bjarne Stroustrup 于 年在贝尔实验室开始设计开发的.C++ 进一步扩充和完善了 C 语言,是一种面向对象的程序设计语言.C++ 可运行于多种平台上,如 ...

  9. opencv显示图像

    使用imshow函数 imshow函数功能 imshow的函数功能也非常简单,名称也可以看出来,image show的缩写.imshow负责的就是将图片显示在窗口中,通过设备屏幕展现出来.与imrea ...

  10. (综合)P2089 烤鸡

    题解: 错误的: #include<stdio.h>int n,ret=0,a[10000][10];int p(int c,int s){ int i; for(i=1;i<=3; ...