为什么红黑树如此受欢迎
创始人
2024-05-31 20:35:43
0

平衡二叉查找树有很多,但是我们一提到平衡二叉查找树,常提及的就是红黑树,它的“出镜率”甚至要高于平衡二叉查找树。

红黑树是一种相对平衡的二叉查找树,不符合严格意义上平衡二叉查找树的定义。

目录

红黑树的插入

 红黑树的验证


红黑树的性质

  • 每个结点不是红色就是黑色
  • 根节点是黑色的 
  • 任何上下相邻的节点不能同时为红色 如果一个节点是红色的,则它的两个孩子结点是黑色的 
  •  对于每个结点,从该结点到其所有后代叶结点的所有路径,都包含相同数目的黑色结点 
  • 每个叶子结点都是黑色的空节点,也就是说,叶子节点不存储数据。

满足上面的性质,红黑树就能保证:其最长路径中节点个数不会超过最短路径节点个数的两倍。

红黑树的性能分析

假设每条路径上的黑节点是N,最短的路径全是黑节点,最长的路径节点一个黑节点一个红节点相间 ,N<=任意路径长度<=2N。

平衡二叉查找树的提出是为了解决二叉查找树因为动态更新导致的性能退化问题。因此,“平衡”可以等价为性能不退化,“近似平衡”就等价为性能退化不太严重。二叉查找树的很多操作的时间复杂度与树的高度成正比。一颗极其平衡的二叉树(满二叉树或完全二叉树)的高度大约是log n,如果要证明红黑树是近似平衡的,只需要证明红黑树的高度近似于log n(比如同一量级)。

证明:

 红黑树的最长路径不会超过2log n,也就是说,红黑树的高度不会超过2log n。红黑树只比高度平衡的AVL树高了一倍,因此损失的性能并不多。而相对于AVL树,红黑树维护成本更低,因此,性能并不比AVL树差。

所以为什么红黑树如此受欢迎呢? 

AVL树是一种高度平衡的二叉树,查找数据的效率非常高,但是,AVL树为了维持这种高度的平衡,需要付出更多的代价。为了维持平衡性,每次插入、删除数据都要对树中节点的分布做调整,操作复杂、耗时。
红黑树只做到了近似平衡,并没有做到严格定义上的平衡,因此,维护平衡性的成本比AVL树要低,但性能又损失不大。对于工程应用,我们更倾向于维护成本和性能相对折中的红黑树。更加重要的一点是,大部分编程语言提供了封装了红黑树实现的类,我们直接拿来用即可,不需要从零开始实现,大大节省了开发时间。红黑树是一种近似平衡的二叉查找树。 它是为了解决二叉查找树动态数据更新导致的性能退化问题而创造的。红黑树的高度近似于logn,插入、删除和查找操作的时间复杂度都是O(logn)。
 

红黑树节点的定义 

   // 节点的颜色
    enum Color { RED, BLACK };


    // 红黑树节点的定义
    template
    struct RBTreeNode
    {
        RBTreeNode(const T& data = T(),Color color = RED)
            : _pLeft(nullptr), _pRight(nullptr), _pParent(nullptr)
            , _data(data), _color(color)
        {}
        RBTreeNode* _pLeft;   // 节点的左孩子
        RBTreeNode* _pRight;  // 节点的右孩子
        RBTreeNode* _pParent; // 节点的双亲
        T_data;            // 节点的值域
        Color _color;               // 节点的颜色
    };

在插入节点时,我们将节点的默认颜色给成红色的,因为每条路径上黑色节点相同,如果将节点给成黑色的,就会影响每一条路径,将新节点给成红色如果不符合规则,只需要调整这一条或者相邻路径,将新节点给成红色比将新节点给成黑色代价要小得多。

红黑树的插入

红黑树是在二叉搜索树的基础上加上其平衡限制条件,因此红黑树的插入可分为两步:

1. 按照二叉搜索的树规则插入新节点

2.检测新节点插入后,红黑树的性质是否造到破坏

因为新节点的默认颜色是红色,如果其双亲节点的颜色是黑色,没有违反红黑树任何性质,则不需要调整;但当新插入节点的双亲节点颜色为红色时,就违反了红黑树的性质,不能有连在一起的红色节点,此时需要对红黑树分情况来讨论:

cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点

cur节点不一定是新增节点,有可能是调整之后指向的节点。

情况一: cur为红,p为红,g为黑,u存在且为红

解决方式:将p,u改为黑,g改为红,然后把g当成cur,继续向上调整。

情况二: cur为红,p为红,g为黑,u不存在/u存在且为黑 

u的情况有两种
1.如果u节点不存在,则cur一定是新插入节点,因为如果cur不是新插入节点,则cur和p一定有一个节点的颜色是黑色,就不满足性质∶每条路径黑色节点个数相同。
2.如果u节点存在,则其一定是黑色的,那么cur节点原来的颜色一定是黑色的,现在看到其是红色的原因是因为cur的子树在调整的过程中将cur节点的颜色由黑色改成红色。

解决方式:p为g的左孩子,cur为p的左孩子,则进行右单旋转;相反, p为g的右孩子,cur为p的右孩子,则进行左单旋转 p、g变色--p变黑,g变红 

情况三: cur为红,p为红,g为黑,u不存在/u存在且为黑

p为g的左孩子,cur为p的右孩子,则针对p做左单旋转;相反, p为g的右孩子,cur为p的左孩子,则针对p做右单旋转,则转换成了情况2

代码实现:

bool Insert(const T& data){if (_root == nullptr){_root = new Node(data);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (data > cur->_data){parent = cur;cur = cur->_right}else if (data < >> cur->_data){parent = cur;cur = cur->_left}else {return false;}}//插入节点cur = new Node(data);cur->_col(RED);if (parent->data < data){parent->_right = cur;cur->_parent = parent;}else{parent->_left = cur;cur->_parent = parent;}//控制平衡while (parent && parent->_col == RED){Node* grandfather = parent->_parent;if (parent == grandfather->_left){Node* uncle = parent->_right;//1.uncle存在且为红if (uncle && uncle->_col == RED){//变色+继续向上处理parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else//2.uncle不存在/uncle存在且为黑{//        g//     p//  c//        g//     p//        cif (cur == parent->_left){//右旋RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}if (cur == parent->_right){//先左旋再右旋RotateL(parent);RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}break;}}else // parent == grandfather->_right{Node* uncle = grandfather->_left;if (uncle && uncle->_col == RED){// 变色+继续向上处理parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else // 2 + 3、uncle不存在/ 存在且为黑{//  g    //     p//        c//  g//     p//  cif (cur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{RotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return true;}

 红黑树的验证

红黑树的检测分为两步:

1. 检测其是否满足二叉搜索树(中序遍历是否为有序序列)

2. 检测其是否满足红黑树的性质

bool IsBalance(){if (_root && _root->_col == RED){cout << "根节点不是黑色" << endl;return false;}// 最左路径黑色节点数量做基准值int banchmark = 0;Node* left = _root;while (left){if (left->_col == BLACK)++banchmark;left = left->_left;}int blackNum = 0;return _IsBalance(_root, banchmark, blackNum);}bool _IsBalance(Node* root, int banchmark, int blackNum){if (root == nullptr){if (banchmark != blackNum){cout << "存在路径黑色节点的数量不相等" << endl;return false;}return true;}if (root->_col == RED && root->_parent->_col == RED){cout << "出现连续红色节点" << endl;return false;}if (root->_col == BLACK){++blackNum;}return _IsBalance(root->_left, banchmark, blackNum)&& _IsBalance(root->_right, banchmark, blackNum);}

相关内容

热门资讯

谁能给我发几个好看的穿越文,女... 谁能给我发几个好看的穿越文,女主要聪明点的,还有请给我发下《蛇蝎皇后》这篇小说我有比较多的小说,但是...
网络语叫粉丝是什么意思? 网络语叫粉丝是什么意思?'粉丝’是英语‘Fans’(狂热、热爱之意,后引申为影迷、追星等意思)的音译...
你们希望柯南的最后大结局是新兰... 你们希望柯南的最后大结局是新兰永恒吗肯定滴啊,如果新兰都不永恒了谁还相信爱情、、、 上次预告还有五年...
出师表朗诵 出师表朗诵出师表的朗诵应该配上什么音乐,或者歌曲(最好是现代的),给点建议!新三国片头曲吧试试王宗贤...
《率土之滨》平民新手开局怎么玩... 《率土之滨》平民新手开局怎么玩?前期开荒核心是完美的利用每一点资源,把他变为你前期最有利的抢地武器!...
咒怨里面的白老妇[那个鬼 的扮... 咒怨里面的白老妇[那个鬼 的扮演者是谁?告诉我吧求求你们了 我要是不知道他是活人演的拿篮球的那个老婆...
体验当家的辛苦 体验当家的辛苦自己体会会有灵感的。什么事情还是自己做一下比较真实这样的作文就必须要自己去亲身体会,让...
(只要人物时间地点的正确就能开... (只要人物时间地点的正确就能开启尘封已久的记忆)能解释一下这个是什么含义是一个女的写给我的是表白吗大...
科学和迷信你们信哪个?不能解释... 科学和迷信你们信哪个?不能解释的东西事物算得上是迷信吗迷信我是不会相信的,如果对所谓的科学百分之百的...
请问延世大学韩语教程和标准韩国... 请问延世大学韩语教程和标准韩国语哪本更适合自学?哪本语法更详细,更易于学习。先谢谢啦。你好,标准韩国...
有谁曾经暗恋一个人,很久都没有... 有谁曾经暗恋一个人,很久都没有见到却还是很想念他想就想呗,随自己的意就可以了让她留在心里吧.我也曾暗...
昨天在车上看的碟子,好像是,一... 昨天在车上看的碟子,好像是,一个DJ现场,一个人边唱边喝酒,还互动问题,答完就喝。提问的歌曲有:洪湖...
魔兽世界小白任务和战场问题 魔兽世界小白任务和战场问题去魔兽数据库里找 多玩 嘟牛 这两个网站的 数据库很全 什么任务...
《公主回宫》什么时候开播? 《公主回宫》什么时候开播?已经开播了,祝你愉快.
到底大灰狼和小绵羊是一对,还是... 到底大灰狼和小绵羊是一对,还是和小白兔是一对很显然小绵羊和小白兔是一对
你觉着《他来了请闭眼》中霍建华... 你觉着《他来了请闭眼》中霍建华演技如何?《他来了请闭眼》中霍建华演技很好。霍建华的演技很好,能够表现...
九色神鹿故事? 九色神鹿故事?很久以前,在恒河边上有一只九种毛色的鹿,它那闪闪发光的鲜艳毛色和洁白如雪的美丽鹿角,以...
刘州成坚强的故事 刘州成坚强的故事就是男子汉一点的 刘小美的刘州成他是一个很坚强的人来的.. 一路走来他都是勇敢地走来...
书籍设计的内容简介 书籍设计的内容简介《书籍设计》立足于新世纪中国艺术教育的改革,将艺术理论与技能培训融会贯通,从内容选...
2024年山西中考初二考地理生... 2024年山西中考初二考地理生物吗不考。截止2022年6月1日山西中考除晋中和阳泉两个改革试点区域外...