红黑树的概念与实现

这篇具有很好参考价值的文章主要介绍了红黑树的概念与实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

红黑树的概念与实现

目录

​一、红黑树的概念

1.什么是红黑树

2.红黑树满足的性质

3.红黑树存在的意义

二、红黑树的实现

1.类的构建

2.插入函数

(1)插入一个节点

(2)调整节点

(3)旋转

三、红黑树的检验


一、红黑树的概念

1.什么是红黑树

红黑树是一种二叉搜索树,每个结点增加一个变量表示结点的颜色,颜色只能是Red或Black。 通过对所有从根到叶子节点的路径上各个结点颜色的限制,保证没有一条路径会比其他路径长出两倍,因而红黑树是一种接近平衡的二叉搜索树。

2.红黑树满足的性质

(1)每个结点不是红色就是黑色

(2)根节点是黑色的 

(3)如果一个节点是红色的,则它的两个子结点是黑色的(可以出现连续的黑节点,但不可以出现连续的红节点)

(4)对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点 

(5)每个叶子结点都是黑色的(此处的叶子结点指的是null结点)

红黑树的五个规则使得搜索树成为了一个不严格的AVL树。

红黑树的概念与实现

3.红黑树存在的意义

对于AVL树,由于它是绝对平衡的,所以为了维持这样的结构它就需要大量地对节点进行旋转,而红黑树对平衡的要求不是很严格,需要旋转的次数就减少了。同时,AVL树相比红黑树的搜索效率也并没有明显提高,比如说在同样的多个数据中查找一个数据,AVL树查找需要10次递归,在红黑树中查找可能需要递归15次(大于等于十次),但是对于一个每秒能计算一亿次的计算机而言,这五次查找根本就没有时间的影响。

所以红黑树是一种接近平衡的二叉搜索树,它不如AVL树的结构严格平衡,也解决了普通二叉搜索树的极端情况搜索效率下降的问题。

二、红黑树的实现

1.类的构建

只要是树就一定需要节点类BRTreeNode和整棵树的类BRTree,并将它们放在一个命名空间内。

BRTreeNode类中的成员变量包括,一个pair键值对(_kv),父指针(_parent),左子节点指针(_left),右子节点指针(_right),还有一个表示颜色的变量(_col),用枚举变量实现。

这里有个小问题,我们在构造函数中_col应该初始化为红还是黑呢?如果我们把这个默认颜色设置为黑,那么只要增加一个黑节点那一定会违背第四个规则。(对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点 )而设为红节点,在后来的调整中还可以改,故默认颜色为红。

#include<assert.h>
namespace MY_BRTree
{
    //节点只有黑色和红色
    enum Colour
    {
        RED,
        BLACK,
    };
    
    template<class K, class V>
    struct BRTreeNode
    {
        std::pair<K, V> _kv;
        BRTreeNode* _parent;
        BRTreeNode* _left;
        BRTreeNode* _right;
        Colour _col;
        
        BRTreeNode(const std::pair<K, V>& kv)
            :_kv(kv)
            , _left(nullptr)
            , _right(nullptr)
            , _parent(nullptr)
            , _col(RED)
        {}
        //除了键值对直接初始化,指针为空,默认颜色设置为红
    };
    
    template<class K, class V>
    class BRTree
    {
        typedef BRTreeNode<K, V> Node;
    public:
        //成员函数
    private:
        Node* _root = nullptr;
    }                

}

2.插入函数

我们的红黑树依旧只学习插入函数

(1)插入一个节点

bool insert(std::pair<K, V>& kv)
{
    //空树特殊处理
    if (_root == nullptr)
    {
        _root = new Node(kv);
        _root->_col = BLACK;//根是黑色的
        return true;
    }
    //非空树
    else
    {
        Node* parent = nullptr;
        Node* cur = _root;//父子指针查找迭代,小往左走,大往右走
        while (cur)
        {
            if (kv.first < cur->_kv.first)
            {
                parent = cur;
                cur = cur->_left;
            }
            else if (kv.first > cur->_kv.first)
            {
                parent = cur;
                cur = cur->_right;
            }
            else
            {
                return false;
            }
        }
        
        //此时已经找到了空节点
        cur = new Node(kv);
        cur->_col = RED;//设置初始颜色为红
        
        //将新节点与其父节点连接起来
        if (kv.first < cur->_kv.first)
        {
            parent->_left = cur;
            cur->_parent = parent;
        }
        else
        {
            parent->_right = cur;
            cur->_parent = parent;
        }
        
    }  
    //到这里我们完成了节点的插入,后续还需要对树进行调整,主要是对节点颜色的调整                                                                                                                                                                                                    

                    
}

(2)调整节点

红黑树的节点颜色调整参考新插入节点的祖父节点的另一棵子树的根节点,也就相当于我们辈分中的叔叔节点。情况总共分为六种:

红黑树的概念与实现

(3)旋转

上面的六种需要旋转的情形中当然也需要六种不同的解决方式。

所有的旋转只需要使用AVL树的左右单旋函数即可,这里我们只讲解4、5、6三种情况。

首先,图4,uncle节点在右且为红。

将parent和uncle节点变红且grandfather节点变红,如果grandfather的父节点也为红就继续向上更新至不会出现连续红节点即可。

红黑树的概念与实现

其次,uncle在右为黑或者为空(空也为黑,这里仅以存在举例)而且cur、parent、grandfather在一条直线上。先对grandfather节点进行右单旋,然后改cur和grandfather为红节点,parent为黑节点即可。

红黑树的概念与实现

然后,uncle在右为黑或者为空(空也为黑,这里仅以存在举例)而且cur、parent、grandfather不在一条直线上。先对parent节点进行左单旋变成上面的情况,然后再对grandfather右单旋再正确改变节点的颜色即可。

红黑树的概念与实现

最后,uncle在左边的情况沿用上面的解法,将旋转方向改变一下,以同样的方式改变颜色就可以了。

最后的代码实现:

bool insert(std::pair<K, V>& kv)
{
    if (_root == nullptr)
    {
        _root = new Node(kv);
        _root->_col = BLACK;//根是黑色的
        return true;
    }
    else
    {
        Node* parent = nullptr;
        Node* cur = _root;
        while (cur)
        {
            if (kv.first < cur->_kv.first)
            {
                parent = cur;
                cur = cur->_left;
            }
            else if (kv.first > cur->_kv.first)
            {
                parent = cur;
                cur = cur->_right;
            }
            else
            {
                return false;
            }
        }
        
        cur = new Node(kv);
        cur->_col = RED;

        if (kv.first < cur->_kv.first)
        {
            parent->_left = cur;
            cur->_parent = parent;
        }
        else
        {
            parent->_right = cur;
            cur->_parent = parent;
        }

        while (parent && parent->_col == RED)
        {
            //可以存在多个连续的黑节点,单不能出现连续的红节点
            //红黑树对于树的处理只取决于插入节点的祖父节点的另一个子节点,相当于叔叔节点

            Node* grandfater = parent->_parent;
            //叔叔节点在右侧
            if (parent == grandfater->_left)
            {
                Node* uncle = grandfater->_right;
                //uncle存在且为红
                if (uncle && uncle->_col == RED)
                {
                    uncle->_col = BLACK;
                    parent->_col = BLACK;
                    grandfater->_col = RED;

                    cur = grandfater;
                    parent = cur->_parent;
                }
            }
            else
            {
            //uncle存在且为黑或者uncle不存在
            //cur是parent的左子节点
            if (cur == parent->_left)
            {
                RotateR(grandfater);
            parent->_col = BLACK;
            grandfater->_col = RED;
            }
            //cur是parent的右子节点
            else
            {
                RotateL(parent);
            RotateR(grandfater);
            cur->_col = BLACK;
            grandfater->_col = RED;
            }
            break;
            }
            }
            //叔叔节点在左侧
            else
            {
                Node* uncle = grandfater->_right;
                //uncle存在且为红
                if (uncle && uncle->_col == RED)
                {
                    uncle->_col = BLACK;
                    parent->_col = BLACK;
                    grandfater->_col = RED;

                    cur = grandfater;
                    parent = cur->_parent;
                }
                else
                {
                    //uncle存在且为黑或者uncle不存在
                    //cur是parent的右子节点
                    if (cur == parent->_left)
                    {
                        RotateL(grandfater);
                        parent->_col = BLACK;
                        grandfater->_col = RED;
                    }
                    //cur是parent的左子节点
                    else
                    {
                        RotateR(parent);
                        RotateL(grandfater);
                        cur->_col = BLACK;
                        grandfater->_col = RED;
                    }
                    break;
                }    
            }
         }
        _root->_col = BLACK;//不管我改没改根,我都把根置为黑,保证根必为黑
        return true;
    }
}

三、红黑树的检验

我们之前学过AVL树的检测(平衡一因子正确且为-1,0,1其一),红黑树的检验就比较复杂了。

红黑树必须满足红黑树的四个必要条件:

  • 根节点是黑色的 
  • 如果一个节点是红色的,则它的两个子结点是黑色的(可以出现连续的黑节点,但不可以出现连续的红节点)
  • 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点 
  • 每个叶子结点都是黑色的(此处的叶子结点指的是null结点)

所以我们可以从这四条的方向考虑。

其中,由于所有的叶子节点都是nullptr,它一定为黑节点,所以第四条不需要写入判断。

bool IsBalance()
{
    if (_root == nullptr)//空树不是红黑树
    {
        return true;
    }

    if (_root->_col != BLACK)//根节点不为黑一定不是红黑树,条件1
    {
        return false;
    }
    
    int ref = 0;//ref用于储存最左侧路径上的黑节点数目
    Node* left = _root;
    while (left)
    {
        if (left->_col == BLACK)
        {
            ++ref;
        }
        left = left->_left;
    }
    return Check(_root, 0, ref);
    //Check函数用于检验其他路径上黑节点数是否相同以及是否有连续红节点,条件2和3
}

再来看看Check,Check会一直向下通过黑节点的个数检测各个子树是否为红黑树。文章来源地址https://www.toymoban.com/news/detail-431352.html

bool Check(Node* root, int blackNum, const int ref)
{
    if (root == nullptr)//找到了空树,此时已经统计完了一条路径上的黑节点数
    {
        if (blackNum != ref)//黑节点数与当前路径不同就不是红黑树
        {
            std::cout << "该路径上的黑节点数不等于最左侧路径的黑节点数" << std::endl;
        return false;
        }
        return true;
    }
    
    if (root->_col == RED && root->_parent->_col == RED)//不能有连续红节点
    {
        std::cout << "出现连续的红节点,不符合规范" << std::endl;
        return false;
    }

    if (root->_col == BLACK)//走到这里就一定没统计完整个路径,继续统计
    {
        ++blackNum;
    }

    return Check(root->_left, blackNum, ref) && Check(root->_left, blackNum, ref);
    //继续递归左树和右树
}

到了这里,关于红黑树的概念与实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++】红黑树的原理与实现

      文章目录 一、引言 二、红黑树的概念与性质 2、1 红黑树的概念 2、2 红黑树的性质 三、红黑树的定义与实现 3、1 红黑树的定义 3、2 插入新节点 3、2、1 默认插入红色节点 3、3 插入情况分类 3、3、1 情况一(根据颜色向上调整) 3、3、2 情况二(单次旋转+变色) 3、3、3 情

    2024年02月13日
    浏览(33)
  • 【数据结构】二叉树---红黑树的实现

    目录 一.  红黑树的概念及性质 二.  红黑树结点结构的定义 三.  红黑树的插入操作      1. 情况一      2. 情况二        3. 情况三 四.  红黑树的验证 五.  红黑树与AVL树的比较 红黑树是一种自平衡的二叉搜索树,它在每个节点上增加了一个存储位来表示节点的颜色,

    2024年03月21日
    浏览(37)
  • 二叉搜索树:红黑树的原理和实现

    💭上文我们在遇到问题:二叉搜索树退化到单支导致效率和性能降低时,利用了AVL树解决。但是由于AVL树是一棵绝对平衡的树,每次修改树结构都要保证左右子树高度差的绝对值不超过1,这可能会引发多次旋转。因此,若我们要设计出一棵结构动态变化的二叉搜索树,利用

    2024年02月01日
    浏览(29)
  • Java 数据结构篇-实现红黑树的核心方法

      🔥博客主页: 【 小扳_-CSDN博客】 ❤感谢大家点赞👍收藏⭐评论✍      文章目录         1.0 红黑树的说明         2.0 红黑树的特性         3.0 红黑树的成员变量及其构造方法         4.0 实现红黑树的核心方法         4.1 红黑树内部类的核心方法         (1)判

    2024年01月24日
    浏览(32)
  • 红黑树的使用场景

       

    2024年02月15日
    浏览(26)
  • 红黑树(AVL树的优化)上

    红黑树略胜AVL树 AVL树是一颗高度平衡搜索二叉树: 要求左右高度差不超过1(严格平衡) 有的大佬认为AVL树太过严格,对平衡的要求越严格,会带来更多的旋转(旋转也还是会有一定的消耗!!) 红黑树的出发点: 最长路径不超过最短路径的2倍(近似平衡) 相对而言,插

    2024年02月10日
    浏览(29)
  • 数据结构之进阶二叉树(二叉搜索树和AVL树、红黑树的实现)超详细解析,附实操图和搜索二叉树的实现过程图

    绪论​ “生命有如铁砧,愈被敲打,愈能发出火花。——伽利略”;本章主要是数据结构 二叉树的进阶知识,若之前没学过二叉树建议看看这篇文章一篇掌握二叉树,本章的知识从浅到深的 对搜索二叉树的使用进行了介绍和对其底层逻辑的实现进行了讲解 ,希望能对你有所

    2024年02月04日
    浏览(36)
  • 【C++】红黑树的插入分析及验证

    红黑树 是一种二叉搜索树, 但在每个节点上增加一个存储位表示节点的颜色,可以是red或black, 通过对任何一条从根到叶子的路径上各个节点着色的方式的限制,红黑树确保没有一条路径会比其他路径长处两倍,所以是接近平衡的 1. 每个结点不是红色就是黑色 2. 根节点是黑

    2024年02月05日
    浏览(32)
  • 【C++】AVL树和红黑树的插入

    时间过的好快,我也修炼到红黑树了 人世这一遭,何其短暂而漫长啊…… 1. 虽然二叉搜索树的搜索效率很高,当搜索树接近满二叉树时,搜索效率可以达到logN,但是如果数据是有序的,则二叉搜索树会退化为单支树,搜索效率和普通的序列式容器相同了就,所以在搜索树的

    2023年04月08日
    浏览(30)
  • B树、B+树 、红黑树的概念及区别

    B树是一种自平衡的搜索树,广泛应用于文件系统和数据库中。B树的特点是: 根节点至少有两个子节点; 除根节点和叶子节点外,每个节点至少有m个子节点,其中m称为B树的阶; 所有叶子节点都在同一层; 每个节点存储的个数必须满足:$$lceilfrac{m}{2}rceil-1leqslant

    2024年02月12日
    浏览(25)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包