我的编程空间,编程开发者的网络收藏夹
学习永远不晚

C++数据结构之红黑树的实现

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

C++数据结构之红黑树的实现

一、什么是红黑树

红黑树在表意上就是一棵每个节点带有颜色的二叉搜索树,并通过对节点颜色的控制,使该二叉搜索树达到尽量平衡的状态。所谓尽量平衡的状态就是:红黑树确保没有一条路径比其他路径长两倍。

和AVL树不同的是,AVL树是一棵平衡树,而红黑树可能平衡也可能不平衡(因为是尽量平衡的状态)

二、红黑树的约定

要实现一棵红黑树,即要红黑树确保没有一条路径比其他路径长两倍。通过对节点颜色的约定来实现这一目标。

1.根节点是黑色的。

2.如果一个节点是红色的,则它的两个孩子都是黑色的。

3.对于每个节点,从该节点到其所有后代节点的简单路径上,均包含相同数量的黑色节点。

实现了这三条颜色规则的二叉搜索树,即也实现了没有一条路径比其他路径长两倍,即实现了一棵红黑树。

三、红黑树vsAVL

1、调整平衡的实现机制不同

红黑树根据节点颜色(同一父节点出发到叶子节点,所有路径上的黑色节点数目一样),一些约定和旋转实现。

AVL根据树的平衡因子(所有节点的左右子树高度差的绝对值不超过1)和旋转决定。

2、红黑树的插入效率更高

红黑树是用非严格的平衡来换取增删节点时候旋转次数的降低,任何不平衡都会在三次旋转之内解决,红黑树并不追求“完全平衡”,它只要求部分地达到平衡要求,降低了对旋转的要求,从而提高了性能

而AVL是严格平衡树(高度平衡的二叉搜索树),因此在增加或者删除节点的时候,根据不同情况,旋转的次数比红黑树要多。所以红黑树的插入效率更高

3、AVL查找效率高

如果你的应用中,查询的次数远远大于插入和删除,那么选择AVL树,如果查询和插入删除次数几乎差不多,应选择红黑树。即,有时仅为了排序(建立-遍历-删除),不查找或查找次数很少,R-B树合算一些。

四、红黑树的实现

实现一棵红黑树,本质是实现它的插入函数,使插入函数可以实现红黑树的颜色约定,它的实现分为两步,即先找到插入的位置,再控制平衡。插入函数返回值设计为bool,插入成功返回true,失败返回false。控制平衡时,需要关注四个节点,即新插入的节点,它的父节点,它的叔叔节点,它的祖父节点。

1.找到插入的位置

当为第一个节点的时候,颜色设为黑,直接插入。

当插入的不是第一个节点时,颜色设为红,需要根据二叉搜索树的性质找到插入位置。并实现三叉链。

        if (_root == nullptr)
        {
            _root = new Node(kv);
            _root->_col = Black;
            return true;
        }
        Node* parent = nullptr;
        Node* cur = _root;
        while (cur)
        {
            if (cur->_kv.first < kv.first)
            {
                parent = cur;
                cur = cur->_right;
            }
            else if (cur->_kv.first > kv.first)
            {
                parent = cur;
                cur = cur->_left;
            }
            else
            {
                return false;
            }
        }
        cur = new Node(kv);
        cur->_col= Red;
        if (parent->_kv.first < kv.first)
        {
            parent->_right = cur;
            cur->_parent = parent;
        }
        else
        {
            parent->_left = cur;
            cur->_parent = parent;
        }

2.控制平衡

(1)当父节点为黑

当父节点为黑的时候,由于插入的子节点的颜色为红,对三个约定没有任何影响,因此不需要调整平衡。

(2)判断父节点在祖父节点的位置

通过判断父节点在祖父节点的位置,来定义叔叔节点的位置,以及之后的旋转方向的判断。

while (parent && parent->_col == Red)
{
Node* grandfather = parent->_parent;
if (parent == grandfather->_left)
{
   Node* uncle = grandfather->_right;
   //三种情况的处理
}
else
{
   Node* uncle = grandfather->_left;
   //三种情况的处理
}

首先需要使用大循环,这个循环是为情况1而准备的,情况2和3直接跳出循环即可,因为只有情况1对上层循环有影响。
下面我们以父节点在祖父节点的左侧为例,右侧同理。

(3)叔叔节点存在且为红

解决方案:将父节点和叔叔节点设为黑,将祖父节点设为红。然后将祖父节点作为新节点继续向上平衡。如果祖父节点是根节点,那么需要再将其置为黑。

注意,这种情况和其他情况不同的是,需要将祖父节点作为新插入的节点继续向上遍历,这说明需要一个循环。而其他类型的情况直接break跳出这个循环即可。

//第一种情况
if (uncle && uncle->_col == Red)
{
    parent->_col = uncle->_col = Black;
    grandfather->_col = Red;
    cur = grandfather;
    parent = cur->_parent;
}

这种情况只需要控制颜色即可,但是要继续向上循环。

(4)父节点为红,叔叔不存在或存在且为黑,新插入的节点在父节点左侧

解决方案:对祖父节点右旋操作,并将祖父节点置为红,父节点置为黑。

关于旋转的细节,我在AVL树中有详细的解释:C++手撕AVL树

//第二种情况,右单旋
if (cur == parent->_left)
{
RotateR(grandfather);
parent->_col = Black;
grandfather->_col = Red;
}

(5)父节点为红,叔叔不存在或存在且为黑,新插入的节点在父节点右侧

解决方案:进行双旋,即对父节点进行左单旋,祖父节点进行右单旋。将子节点置为黑,将祖父节点置为红。

else
{
RotateL(parent);
RotateR(grandfather);
cur->_col = Black;
grandfather->_col = Red;
}

(6)最后置黑

每一次插入都对根节点置为黑操作,因为第一种情况可能导致根节点不是黑。同时对根节点置黑也并不违反三条规定。

3.测试代码

当我们处理完父节点在祖父节点的左侧后,处理父节点在祖父节点的右侧。

全部处理之后,我们的插入代码就完成了,接下来要对整个树进行测试,即对三个约定来进行测试:

1.当根节点为红时,返回false。

2.判断一个节点和其父节点的颜色是否都为红,若都为红返回false。

3.以最左的一条路径上的根节点数量为基准,通过递归遍历每一条路径上的根节点的数量,当每条路径遍历节点到空时,将两者进行比较,如果最终结果不相等则返回false。

    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);
    }

五、完整代码

1.test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"RBtree.h"
#include<vector>
int main()
{
    RBTree<int, int> t;
    vector<int> v;
    srand(time(0));
    int N = 100000;
    int count = 0;
    for (int i = 0; i < N; i++)
    {
        v.push_back(rand());
    }
    for (auto e : v)
    {
        pair<int,int> kv(e, e);
        t.insert(kv);
        if (t.IsBalance())
        {
            cout << "insert" << e << endl;
            count++;
            cout << count << endl;
        }
        else
        {
            cout << e << "插入失败" << endl;
            cout << "不是平衡的" << endl;
            break;
        }
    }
}

2.RBTree.h

#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
enum Color
{
    Red,
    Black
};
template<class K,class V>
struct RBTreeNode
{
    RBTreeNode<K, V>* _left;
    RBTreeNode<K, V>* _right;
    RBTreeNode<K, V>* _parent;
    pair<K, V> _kv;
    Color _col;
    RBTreeNode(pair<K, V>& kv)
        :_left(nullptr)
        , _right(nullptr)
        , _parent(nullptr)
        , _col(Red)
        , _kv(kv)
    {}
};
template<class K,class V>
struct RBTree
{
    typedef RBTreeNode<K, V> Node;
private:
    Node* _root;
public:
    RBTree()
        :_root(nullptr)
    {}
    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);
    }
    //右单旋
    void RotateR(Node* parent)
    {
        Node* cur = parent->_left;
        Node* curL = cur->_left;
        Node* curR = cur->_right;
        Node* parentParent = parent->_parent;
        parent->_left = curR;
        if (curR)
            curR->_parent = parent;
        cur->_right = parent;
        parent->_parent = cur;
        if (parent == _root)
        {
            _root = cur;
            _root->_parent = nullptr;
        }
        else
        {
            if (parentParent->_left == parent)
            {
                parentParent->_left = cur;
                cur->_parent = parentParent;
            }
            else if (parentParent->_right == parent)
            {
                parentParent->_right = cur;
                cur->_parent = parentParent;
            }
            else
            {
                assert(false);
            }
        }
    }
    //左单旋
    void RotateL(Node* parent)
    {
        Node* cur = parent->_right;
        Node* curL = cur->_left;
        Node* parentParent = parent->_parent;
        parent->_right = curL;
        if (curL)
            curL->_parent = parent;
        cur->_left = parent;
        parent->_parent = cur;
        if (parent == _root)
        {
            _root = cur;
            _root->_parent = nullptr;
        }
        else
        {
            if (parentParent->_left == parent)
            {
                parentParent->_left = cur;
                cur->_parent = parentParent;
            }
            else if (parentParent->_right == parent)
            {
                parentParent->_right = cur;
                cur->_parent = parentParent;
            }
            else
            {
                assert(false);
            }
        }
    }
    bool insert(pair<K, V>& kv)
    {
        if (_root == nullptr)
        {
            _root = new Node(kv);
            _root->_col = Black;
            return true;
        }
        Node* parent = nullptr;
        Node* cur = _root;
        while (cur)
        {
            if (cur->_kv.first < kv.first)
            {
                parent = cur;
                cur = cur->_right;
            }
            else if (cur->_kv.first > kv.first)
            {
                parent = cur;
                cur = cur->_left;
            }
            else
            {
                return false;
            }
        }
        cur = new Node(kv);
        cur->_col= Red;
        if (parent->_kv.first < kv.first)
        {
            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 = grandfather->_right;
                //第一种情况
                if (uncle && uncle->_col == Red)
                {
                    parent->_col = uncle->_col = Black;
                    grandfather->_col = Red;
                    cur = grandfather;
                    parent = cur->_parent;
                }
                else
                {
                    //第二种情况,右单旋
                    if (cur == parent->_left)
                    {
                        RotateR(grandfather);
                        parent->_col = Black;
                        grandfather->_col = Red;
                    }
                    //第三种情况,左右双旋
                    else
                    {
                        RotateL(parent);
                        RotateR(grandfather);
                        cur->_col = Black;
                        grandfather->_col = Red;
                    }
                    break;
                }
                _root->_col = Black;
            }
            else
            {
                Node* uncle = grandfather->_left;
                //第一种情况
                if (uncle && uncle->_col == Red)
                {
                    parent->_col = uncle->_col = Black;
                    grandfather->_col = Red;
                    cur = grandfather;
                    parent = cur->_parent;
                }
                else
                {
                    //第二种情况,左单旋
                    if (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;
            }
        }
    }
};

到此这篇关于C++数据结构之红黑树的实现的文章就介绍到这了,更多相关C++红黑树内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

C++数据结构之红黑树的实现

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

C++数据结构之红黑树的实现

红黑树在表意上就是一棵每个节点带有颜色的二叉搜索树,并通过对节点颜色的控制,使该二叉搜索树达到尽量平衡的状态。本文主要为大家介绍了C++中红黑树的原理及实现,需要的可以参考一下
2022-11-13

C++数据结构红黑树的示例分析

这篇文章给大家分享的是有关C++数据结构红黑树的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。概念和性质红黑树的概念: 红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或
2023-06-29

Java数据结构之红黑树的示例分析

小编给大家分享一下Java数据结构之红黑树的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!一、红黑树所处数据结构的位置:在JDK源码中, 有treeMap
2023-05-30

java红黑树的数据结构是什么

Java中的红黑树数据结构是以节点为基础的数据结构,每个节点包含一个键值对和指向其子节点的指针。红黑树的节点类通常包含以下属性:键值对:用于存储节点的键和值。颜色:用于表示节点的颜色,可以是红色或黑色。左子节点和右子节点:分别指向节点的
java红黑树的数据结构是什么
2024-03-13

C++数据结构之AVL树如何实现

这篇文章主要讲解了“C++数据结构之AVL树如何实现”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C++数据结构之AVL树如何实现”吧!1.概念(1)二叉搜索树的缺点要手撕AVL树,我们首先
2023-07-02

用Python实现数据结构之树

树是由根结点和若干颗子树构成的。树是由一个集合以及在该集合上定义的一种关系构成的。集合中的元素称为树的结点,所定义的关系称为父子关系。父子关系在树的结点之间建立了一个层次结构。在这种层次结构中有一个结点具有特殊的地位,这个结点称为该树的根结
2023-01-30

C++ RBTree红黑树的性质与实现

红黑树是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black;通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是平衡的
2023-03-08

Go语言中的红黑树、B Tree、B+Tree等基本数据结构

Go语言中的红黑树、B树和B+树是基本的数据结构,可用于实现高效的查找、插入和删除操作。1. 红黑树(Red-Black Tree)是一种自平衡的二叉查找树。它具有以下特点:- 每个节点要么是红色,要么是黑色。- 根节点是黑色的。- 每个叶
2023-10-12

C++高级数据结构之二叉查找树怎么实现

本文小编为大家详细介绍“C++高级数据结构之二叉查找树怎么实现”,内容详细,步骤清晰,细节处理妥当,希望这篇“C++高级数据结构之二叉查找树怎么实现”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。高级数据结构(Ⅳ)
2023-06-30

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录