浅析红黑树原理以及实现

我们在上一篇博客认识到了平衡二叉树(AVLTree),了解到平衡二叉树的性质,其实平衡二叉树最大的作用就是查找,AVL树的查找、插入

和删除在平均 最坏情况下都是O(logn)。AVL树的效率就是高在这个地方。如果在AVL树中插入或删除节点后,使得高度之差大于1。此

时,AVL树的平衡状态就被破 坏, 它就不再是一棵二叉树;为了让它重新维持在一个平衡状态,就需要对其进行旋转处理, 现在呢,我

们来思考一下虽然AVL树的查找效率高,但是 呢构建 一颗AVL树的成本是多少?? 因为当它的高度差大于1的时候,就要触发旋转算法来

调节平衡二叉树,所以当你数据足够大的时候,那么你创建 颗平衡 二叉树的成本其实不小. 这个时候就有人开始思考,并且提出了

黑树的理论,那么红黑树到底比AVL树好在哪里??

首先我们应该知道由于AVL树的性质,当它的任意两个分支之间的高度差大于一的时候就会触发旋转算法,所以构建一棵树得触发很多次

旋转算法,这里 的AVL树的查找时间复杂度为O(logN),但是 黑树它的性质让它的查找的时间复杂度为O(nlogN).可以看出AVL树时间复

度优于红黑树,BUT!! 在计 算机 中它 的计算速度惊人每秒的运算量都是亿级的,所以nlogN和logN在计算机面前没有什么区别,红

树触发旋转算法的概率要远远小于AVL树,这时 构建一 个红黑树的成本也就远远小于AVL树,所以生活中经常使用的都是红黑树.(AVL

树只是红黑树的一个前身,红黑树就是AVL树的加强版) 首先我们来认识红黑树的性质,红黑树是一颗二叉搜索树,它在每个节点上增

了一个存储为来表示节点的颜色,可以RED或BLACK,通过对任何一条从 根到叶子简单路径上的颜色约束,红黑树保证最长路径不超过

最短路径的两倍,因而近似平衡.
红黑树的具体性质

1.每个节点的颜色不是红色就是黑色.

2.红黑树的根节点必须是黑色的.

3.如果一个节点是红色的,则它的两个子节点是黑色的.

4.对每一个节点,从该节点到齐所有后代节点的简单路径上,均包含相同数目的黑色节点.
其实当我们的上面这些条件满足后,这棵树就是已经是一个最长路径不超过最短路径的两倍了. 具体为什么?? 这里我解释一下.
有这张图我也不需要再解释什么了,你的最短路径就是全黑节点,最长路径就是一个红节点一个黑节点,最后黑色节点相同时,最长路
径刚好是最短路 径的两倍. 可以看到这几个性质看起来没有联系,但是环环相扣最后构成一个红黑树. 所以我们构建红黑树的时候一定
要注意性质.
红黑树的插入


首先红黑树的插入其实不是那么容易实现的,以前搜索树的插入我们很容易理解现在我们首先思考一个问题,你插入节点的默认颜

色是RED或 BLACK?  这里我们需要根据性质来思考,首先如果插入黑节点,这个可以直接插入无论它的父亲是什么颜色,但是红黑

的性质是每条路径的黑色节点数目相同 这个时候你再想想那其他路径的黑色节点数目一定比你现在少一个节点,所以调整起来

非常繁琐的. 插入红节点不需要调整其他路径,如果它的父亲 为黑,那么直接插入,如果他的父亲为红那么在该路径上面开始分

情况调整. 所以插入节点默认颜色一定要为红.如果为黑调节成本太大了. 接下来开始插入节点如果插入节点的父亲为黑那么直接插

入后返回不需要做任何调整. 但是如果插入节点的父亲为红,那么就需要调整了.具体的调整 过程可以分为三个情况:


1. 新插入节点Cur的父亲为红,并且爷爷节点(parentparent)为黑,叔叔节点(uncle)存在且为红.



此时对该子树进行操作,parent节点和uncle节点变为黑,parentparent节点变为红,这样我们就保证该子树中每条路径中黑色节点相同

并且没有 连续的红节点,然后再让cur等于parentparent节点继续往上继续调整,直到该树中没有连续的红节点. 具体的过程如图所示:





2. 新插入节点Cur的父亲为红,并且爷爷节点(parentparent)为黑,叔叔节点(uncle)不存在或存在且为黑.



首先我们要明白,第二种情况一定是第一种情况调整后,然后向上调整的时候所遇到的.也就是说你单纯插入一个节点是不可能碰到

种情况,出现 这种情况的原因就是,第一种情况调整后,它的parentparent节点变为红色,然后才会出现这种情况. 那么为什么呢??

因为就拿上面这个子树来看,如果你cur是新插入的话,明显就很不合理! 因为你的父亲是红的而你的叔叔是黑的! 这条路径很明

显黑节点个 数和别的路径不一样,所以只有一种可能那就是,cur以前是黑节点,因为颜色调整被变成红色,进而触发旋转操作.
然后我们对这棵子树进行操作,这里有一些旋转的知识如果不明白的话,可以去看我得上一个博客:AVL树. 该博客里面对旋转操作有

详细的讲解. 好了我们继续. 拿这个图为例子. 我们需要根据parentparent节点进行右单旋. 然后将parent节点变为黑色,cur节点和

parentparent变为红色. 当然如果parent为parentparent的右,cur为patent的右就是左单旋了. 具体过程如图所示:



2. 新插入节点Cur的父亲为红,并且爷爷节点(parentparent)为黑,叔叔节点(uncle)不存在或存在且为黑.
这种情况乍得一看跟上面一模一样(其实就是一模一样(手动微笑)). 但是我在这里提的是一个特殊一点的情况,因为这里需要触发右

左双旋或者左右 双旋. 我们看下图这种情况,它满足上述情况但是无法触发左单旋或者右单旋.所以我们得对它进行一点操作,让它可

以旋转.



就拿这个图来说,我们先对parent进行左单选,再然后针对parentpatent进行右单旋. 最后进行于上面相同的变色操作即可. 当然右左

双旋步骤也一样


代码实现:
        bool Insert(const K& key, const V& val){//_Insert(_root, x, y);if (_root == NULL){_root = new Node(key, val);_root->_col = BLACK;}Node* cur = _root;Node* parent = cur;while (cur){if (cur->_key > key){parent = cur;cur = cur->_left;}else if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key == key){return true;}}if (parent->_key > key){parent->_left = new Node(key, val);parent->_left->_parent = parent;cur = parent->_left;}else{parent->_right = new Node(key, val);parent->_right->_parent = parent;cur = parent->_right;}//目前父亲节点,插入节点,叔叔节点已经就绪.while(parent && parent->_col == RED){Node* parentparent = parent->_parent;Node* uncle = NULL;if (parentparent->_left == parent)uncle = parentparent->_right;elseuncle = parentparent->_left;if(uncle && uncle->_col == RED){parentparent->_col = RED;parent->_col = BLACK;uncle->_col = BLACK;cur = parentparent;parent = cur->_parent;}else if (uncle == NULL || uncle->_col == BLACK){if (parentparent->_left == parent){if (parent->_left == cur){RotateR(parentparent);parent->_col = BLACK;}else{RotateLR(parentparent);cur->_col = BLACK;}}else{if (parent->_right == cur){RotateL(parentparent);parent->_col = BLACK;}else{RotateRL(parentparent);cur->_col = BLACK;}}parentparent->_col = RED;if (parentparent == _root){_root = parent;}}else{assert(false);}if (_root->_col == RED){_root->_col = BLACK;}}return false;}
红黑树的删除



红黑树的删除有一定的难度,这里我就说的没有这么详细了其实它跟搜索树的删除很类似就是寻找一个替换节点然后删掉它的替换节点

我们要删除del这个时候,直接删除del成本太高了,然后我们寻找一个边缘节点跟他交换,然后再删除它.所以呢,这样删除的成本是

最小的. 删除分三个情况: 

1.del的左为空.

2.del的右为空.

3.del的左右都不为空.


前两种很容易思考,第三种左右都不为空的情况才需要我们的边缘替换法.所以我这里附上搜索二叉树的删除的代码. 红黑树删除的代

码暂时我还没有解决所以这里之后搜索二叉树的删除代码. 我们可以想着思考思考说不定就写完了.


代码实现:

 bool Remove(const K& key){if (_root == NULL){return false;}Node* cur = _root;Node* parent = NULL;while (cur){       if (cur->key > key){parent = cur;cur = cur->_left;}else if (cur->key < key){parent = cur;cur = cur->_right;}else if (cur->key == key){break;}}Node* del = cur;if (cur->_left == NULL) //左为空{if (parent == NULL){_root = cur->_right;if (_root)_root->_parent = NULL;}else{if (parent->_left == cur)parent->_left = cur->_right;else          parent->_right = cur->_right;if (cur->_right)cur->_right->_parent = parent;}}else if (cur->_right == NULL)//右为空{if (parent == NULL){_root = cur->_left;if (_root)_root->_parent = NULL;}else{if (parent->_left == cur)parent->_left = cur->_left;elseparent->_right = cur->_left;if (cur->_left)cur->_left->_parent = parent;} }else //左右都不为空{if (parent == NULL || cur == parent->_left) //如果cur在左边.{del = cur->_right;while (del->_left){del = del->_left;}del->_parent->_left = del->_right;;}else //cur在parent的右边.{del = cur->_left;while (del->_right){del = del->_right;}del->_parent->_right = del->_left;}cur->key = del->key;cur->val = del->val;}delete del;}


编写一个检验该二叉树是否为红黑树的程序:


这里首先解决一个最棘手的问题,如何判断一个路径上面的黑节点相同? 我们很容易想到,可以先遍历一条路径找到一个基准值,然

后和其他路径做比较,代码实现就是每次走到叶子结点的时候,比较该条路径的黑色节点节点个数是否和基准值相等? 如果不相等

那么返回false. 所以我们参数里面需要一个m(传递黑色节点基准值)和n(记录该条路径上面的黑色节点个数). 

那么我们来瞧瞧代码实现:

        bool ISRBtree(){if (_root->_col == RED){return false;}size_t n = 0;size_t m = 0;Node* cur = _root;while (cur){if (cur->_col == BLACK){n++;}cur = cur->_left;}return _ISRBtree(_root, m, n);}bool _ISRBtree(Node* root, size_t m, size_t n){if (root == NULL){if (m == n)return true;elsereturn false;}if (root->_col == BLACK){m++;}if (root->_col == RED && root->_parent && root->_parent->_col == RED){return false;}return _ISRBtree(root->_left, m, n) && _ISRBtree(root->_right, m, n);}

演示结果:


总结


红黑树是一个非常重要的数据结构,现在生活当中的使用我们就能看的出来,可能我们并不需要它的底层构造但是我们要学习一个语

言. 最快的途径就是大师写出来的代码,手头都是资料我们要学习别人的巧妙的地方. 多看源码多多构造经典的算法,或者容器. 

尝试自己编写代码,理解那个框架, 这样对我们的进步都是非常有用的.红黑树目前我就总结这么多,哪里有不足欢迎大家来指出来

,我会虚心改正.


所有代码实现:

#include<iostream>
#include<Windows.h>
#include<assert.h>
using namespace std;enum colour
{RED,BLACK
};template<class K, class V>
struct RBTreeNode
{K _key;K _val;RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;colour _col;RBTreeNode(const K& key, const V& val):_key(key), _val(val), _left(NULL), _right(NULL), _parent(NULL), _col(RED){}
};template<class K, class V>
class RBTree
{typedef RBTreeNode<K, V> Node;
public:RBTree():_root(NULL){}bool Insert(const K& key, const V& val){//_Insert(_root, x, y);if (_root == NULL){_root = new Node(key, val);_root->_col = BLACK;}Node* cur = _root;Node* parent = cur;while (cur){if (cur->_key > key){parent = cur;cur = cur->_left;}else if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key == key){return true;}}if (parent->_key > key){parent->_left = new Node(key, val);parent->_left->_parent = parent;cur = parent->_left;}else{parent->_right = new Node(key, val);parent->_right->_parent = parent;cur = parent->_right;}//目前父亲节点,插入节点,叔叔节点已经就绪.while(parent && parent->_col == RED){Node* parentparent = parent->_parent;Node* uncle = NULL;if (parentparent->_left == parent)uncle = parentparent->_right;elseuncle = parentparent->_left;if(uncle && uncle->_col == RED){parentparent->_col = RED;parent->_col = BLACK;uncle->_col = BLACK;cur = parentparent;parent = cur->_parent;}else if (uncle == NULL || uncle->_col == BLACK){if (parentparent->_left == parent){if (parent->_left == cur){RotateR(parentparent);parent->_col = BLACK;}else{RotateLR(parentparent);cur->_col = BLACK;}}else{if (parent->_right == cur){RotateL(parentparent);parent->_col = BLACK;}else{RotateRL(parentparent);cur->_col = BLACK;}}parentparent->_col = RED;if (parentparent == _root){_root = parent;}}else{assert(false);}if (_root->_col == RED){_root->_col = BLACK;}}return false;}bool ISRBtree(){if (_root->_col == RED){return false;}size_t n = 0;size_t m = 0;Node* cur = _root;while (cur){if (cur->_col == BLACK){n++;}cur = cur->_left;}return _ISRBtree(_root, m, n);}void print(){_print(_root);}protected:void _print(Node* root){if (root == NULL)return;_print(root->_left);cout << root->_val << " ";_print(root->_right);}bool _ISRBtree(Node* root, size_t m, size_t n){if (root == NULL){if (m == n)return true;elsereturn false;}if (root->_col == BLACK){m++;}if (root->_col == RED && root->_parent && root->_parent->_col == RED){return false;}return _ISRBtree(root->_left, m, n) && _ISRBtree(root->_right, m, n);}void RotateLR(Node*& parent){RotateL(parent->_left);RotateR(parent);}void RotateRL(Node*& parent){RotateR(parent->_right);RotateL(parent);}void RotateR(Node*& parent){Node* subL = parent->_left;Node* subLR = NULL;if (subL)subLR = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;Node* ppNode = parent->_parent;subL->_right = parent;parent->_parent = subL;if (ppNode == NULL){_root = subL;_root->_parent = NULL;}else{if (ppNode->_left == parent)ppNode->_left = subL;elseppNode->_right = subL;subL->_parent = ppNode;}}void RotateL(Node*& parent){Node* subR = parent->_right;Node* subRL = NULL;if (subR)subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;Node* ppNode = parent->_parent;subR->_left = parent;parent->_parent = subR;if (ppNode == NULL){_root = subR;_root->_parent = NULL;}else{if (ppNode->_left == parent)ppNode->_left = subR;elseppNode->_right = subR;subR->_parent = ppNode;}}private:Node* _root;
};void Test()
{RBTree<int, int> T;//8 9 2 4 1 6 3 5T.Insert(8, 8);T.Insert(9, 9);T.Insert(2, 2);T.Insert(4, 4);T.Insert(1, 1);T.Insert(6, 6);T.Insert(3, 3);T.Insert(5, 5);cout << "该二叉树是否为红黑树??" << T.ISRBtree() << endl;T.print();system("pause");}


数据结构 — 浅析红黑树原理以及实现相关推荐

  1. 红黑树原理及代码实现(一)

    红黑树简介 红黑树是一种自平衡的二叉查找树,它的统计性能要优于平衡二叉树,因此在很多地方都有应用,例如Java中的TreeMap,HashMap等数据结构都使用到了红黑树.红黑树对很多人来说熟悉而陌生 ...

  2. [Java 8 HashMap 详解系列]7.HashMap 中的红黑树原理

    [Java 8 HashMap 详解系列] 文章目录 1.HashMap 的存储数据结构 2.HashMap 中 Key 的 index 是怎样计算的? 3.HashMap 的 put() 方法执行原 ...

  3. 红黑树 —— 原理和算法详细介绍

    红黑树 -- 原理和算法详细介绍 R-B Tree简介 R-B Tree,全称是Red-Black Tree,又称为"红黑树",它一种特殊的二叉查找树.红黑树的每个节点上都有存储位 ...

  4. 遍历HashMap源码——红黑树原理、HashMap红黑树实现与反树型化(三)

    本章将是HashMap源码的最后一章,将介绍红黑树及其实现,HashMap的remove方法与反树型化.长文预警~~ 遍历HashMap源码--红黑树原理.HashMap红黑树实现与反树型化 什么是红 ...

  5. HashMap红黑树原理解析

    HashMap红黑树原理解析 定义: 简单来说红黑树是一种近视平衡二叉查找树,主要优点是"平衡",即左右子树高度几乎一致,以此来防止树退化为链表,通过这种方式来保障查找的时间复杂度 ...

  6. HashMap底层红黑树原理(超详细图解)+手写红黑树代码

    在看完了小刘老师和黑马的源码视频之后,我整理了一篇HashMap的底层源码文章,学海无涯,这几天看了对红黑树的讲解,故将其整理出来 HashMap底层源码解析上 HashMap底层源码解析下 视频链接 ...

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

    红黑树原理详解及golang实现 文章目录 红黑树原理详解及golang实现 二叉查找树 性质 红黑树 性质 operation 红黑树的插入 `情形1`:空树 `情形2`:插入节点父节为黑色, `情 ...

  8. 数据结构-红黑树原理分析

    前言 在阅读HashMap源码的时候发现,java1.8的HashMap的链表实现增加了红黑树,当链表长度超过指定阈值8的时候回进行树化. 为了提高增删查的效率. 而红黑树又比较复杂,所以专门写一篇关 ...

  9. 浅析红黑树(RBTree)原理及实现

    我们在上一篇博客认识到了平衡二叉树(AVLTree),了解到AVL树的性质,其实平衡二叉树最大的作用就是查找,AVL树的查找.插入和删除在平均和最坏情况下都是O(logn).AVL树的效率就是高在这个 ...

最新文章

  1. 树莓派c语言访问mariadb,树莓派之MariaDB
  2. UML图系列——UML模型图的构成
  3. Python脚本打包成exe文件
  4. 利用FRIDA攻击Android应用程序(二)
  5. jq添加新节点赋予class属性并获取该对象
  6. %=%、%%、%@%、%#%的区别
  7. 2023苏州大学计算机考研信息汇总
  8. 麒麟linux怎么安装软件,中标麒麟Linux操作系统怎么安装软件?
  9. migration java_如何重置migration
  10. ACwing 1018 最低通行费
  11. vs的oxc000007b错误和key valid错误
  12. 实战智能推荐系统(1)-- 个性化推荐系统及其基本推荐算法
  13. 删除office正版增值计划通知的方法
  14. 知道创宇 二级安全公司 骗取面试人源码,长见识了啊。
  15. JDK下载以及安装步骤
  16. OpenXml 操作Excel,Word,PPT
  17. vue拦截器设置请求头失败,laravel设置前端请求头跨域
  18. DGA(域名生成算法)/DNS tunnel
  19. PTA 10-53 查询部分专业的学生
  20. 微信 服务器 台,2W台服务器的微信过载控制系统.docx

热门文章

  1. 【Latex】【插入图片】如何在latex中插入并列图片
  2. python opencv resize函数_OpenCV尺寸调整函数resize
  3. Handler原理讲解及源码分析
  4. 常见的电子器件,这篇文章总结得很到位,你还不收藏吗?
  5. HTTP 500 - Internal Server Error 服务器内部错误
  6. Vue 和 React 的diff有什么不同
  7. SpringBoot#InitBinder
  8. 计算机用户名怎么改好听,电脑版优酷怎么改昵称
  9. 4.6 案例10 使用QList处理数据集
  10. 教会你ECshop安装百度分享按钮教程