【C++】模拟实现红黑树(插入)

news2024/10/7 7:30:35

目录

红黑树的概念

红黑树的性质

红黑树的调整情况

红黑树的模拟实现

枚举类型的定义

红黑树节点的定义

插入函数的实现

旋转函数的实现

左旋

右旋

自检函数的实现

红黑树类


红黑树的概念

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

红黑树的性质

  1. 每个结点不是红色就是黑色

  2. 根节点是黑色的

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

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

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

红黑树的调整情况

  • 新节点插入为红色:

新节点默认着色为红色。

  • 父节点为黑色:

如果新插入节点的父节点是黑色,那么通常只需要进行简单的颜色调整即可,因为黑色节点不会破坏红黑树的性质。

  • 父节点为红色:

这是需要重点关注的情况,因为红色父节点可能违反红黑树的性质。

  • 叔节点也为红色: 如果叔节点(父节点的兄弟节点)也是红色,则将父节点和叔节点都变为黑色,并将祖父节点(父节点的父节点)变为红色。接着,将祖父节点作为新的当前节点,继续向上检查是否破坏了性质。 -叔节点为黑色或不存在: 根据新节点是父节点的左子节点还是右子节点,以及父节点是祖父节点的左子节点还是右子节点,需要进行不同的调整。

  • 父节点是左子节点:

新节点是父节点的右子节点:将父节点进行左旋操作,然后重新从父节点开始着色。 新节点是父节点的左子节点:将父节点变为黑色,将祖父节点变为红色,然后对祖父节点进行右旋操作。

  • 父节点是右子节点:

新节点是父节点的左子节点:将父节点进行右旋操作,然后重新从父节点开始着色。

红黑树的模拟实现

枚举类型的定义

enum Color
 {
  RED,
  BLACK
 };

红黑树节点的定义

  • 成员有:左孩子指针、有孩子指针、父节点指针、一个键值对、颜色(枚举类型)

  • 构造函数:将三个指针置空,颜色默认为红色,由外面传递的键值对作为值

struct RBTreeNode
 {
  RBTreeNode<K, V>* _left;
  RBTreeNode<K, V>* _right;
  RBTreeNode<K, V>* _parent;
  pair<K, V> _kv;
  Colour _color;

  RBTreeNode(const pair<K, V>& kv)
   :_left(nullptr)
   , _right(nullptr)
   , _parent(nullptr)
   , _kv(kv)
   , _color(RED)
  {}
 };

插入函数的实现

  • 函数参数:

const pair<K, V>& kv:要插入到红黑树中的键值对。

  • 检查根节点是否为空:

if (_root == nullptr):如果红黑树为空(即根节点为空),则创建一个新的节点作为根节点,并设置其颜色为黑色。然后返回true表示插入成功。

  • 查找插入位置:

使用parent和cur指针遍历树,查找插入位置。parent用于记录当前节点的父节点,cur用于遍历树。根据键值对的大小关系,将cur指针移动到正确的子树中。如果在遍历过程中发现键值对已经存在于树中(即cur->_kv.first == kv.first),则直接返回false表示插入失败。

  • 创建新节点并插入:

创建一个新的节点cur,并设置其值为kv,颜色为红色。根据之前找到的parent节点的键值对大小关系,将新节点插入到父节点的左子树或右子树中,并更新节点的父节点指针。

  • 调整红黑树:

由于新插入的节点颜色是红色,可能会破坏红黑树的性质。因此,需要进行一系列调整操作来恢复红黑树的性质。while (parent && parent->_color == RED)循环用于检查父节点的颜色,如果父节点是红色的,则需要进行调整。 根据父节点是祖父节点的左子节点还是右子节点,以及新节点是父节点的左子节点还是右子节点,选择不同的调整策略。如果父节点和叔叔节点(祖父节点的另一个子节点)都是红色的,则将父节点和叔叔节点的颜色改为黑色,并将祖父节点的颜色改为红色。然后更新cur和parent指针,继续检查新的父节点。 如果叔叔节点是黑色的,则根据新节点是父节点的左子节点还是右子节点,选择进行左旋或右旋操作,以及可能的颜色调整。在循环结束后,确保根节点的颜色是黑色,这是红黑树的一个基本性质。

  • 返回插入结果:

由于插入操作已经成功完成,无论是否进行了调整操作,都返回true表示插入成功。

bool Insert(const pair<K, V>& kv)
  {
   if (_root == nullptr)
   {
    _root = new Node(kv);
    _root->_color = 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->_color = RED;
   if (parent->_kv.first < kv.first)
   {
    parent->_right = cur;
    cur->_parent = parent;
   }
   else
   {
    parent->_left = cur;
    cur->_parent = parent;
   }

   while (parent && parent->_color == RED)
   {
    Node* grandfather = parent->_parent;
    if (parent == grandfather->_left)
    {

     Node* uncle = grandfather->_right;
     if (uncle && uncle->_color == RED)
     {

      parent->_color = uncle->_color = BLACK;
      grandfather->_color = RED;


      cur = grandfather;
      parent = cur->_parent;
     }
     else
     {
      if (cur == parent->_left)
      {

       RotateR(grandfather);
       parent->_color = BLACK;
       grandfather->_color = RED;
      }
      else
      {

       RotateL(parent);
       RotateL(grandfather);
       cur->_color = BLACK;
       grandfather->_color = RED;
      }

      break;
     }
    }
    else
    {

     Node* uncle = grandfather->_left;
     if (uncle && uncle->_color == RED)
     {

      parent->_color = uncle->_color = BLACK;
      grandfather->_color = RED;


      cur = grandfather;
     }
     else
     {
      if (cur == parent->_right)
      {
       RotateL(grandfather);
       parent->_color = BLACK;
       grandfather->_color = RED;
      }
      else
      {

       RotateR(parent);
       RotateL(grandfather);
       cur->_color = BLACK;
       grandfather->_color = RED;
      }

      break;
     }
    }
   }

   _root->_color = BLACK;

   return true;
  }

旋转函数的实现

  • 这两个函数RotateL和RotateR分别用于执行红黑树的左旋和右旋操作。在红黑树的插入和删除操作中,当破坏了红黑树的性质时,通常需要通过节点的旋转和颜色调整来恢复树的平衡。

左旋

  • 左旋操作通常用于当某个节点的右子节点是红色时。左旋操作将右子节点提升为父节点,并将原父节点降为其左子节点。同时,原父节点的左子节点则成为新父节点的右子节点。

void RotateL(Node* parent)  
{  
    Node* subR = parent->_right; // 右子节点  
    Node* subRL = subR->_left; // 右子节点的左子节点  
  
    parent->_right = subRL; // 原父节点的右子节点指向右子节点的左子节点  
    if (subRL)  
        subRL->_parent = parent; // 更新subRL的父节点指针  
  
    subR->_left = parent; // 右子节点的左子节点指向原父节点  
    parent->_parent = subR; // 更新原父节点的父节点指针  
  
    Node* parentParent = parent->_parent; // 原父节点的父节点  
  
    if (_root == parent) // 如果原父节点是根节点  
    {  
        _root = subR; // 更新根节点为右子节点  
        subR->_parent = nullptr; // 根节点的父节点指针置为null  
    }  
    else  
    {  
        if (parentParent->_left == parent) // 如果原父节点是其父节点的左子节点  
        {  
            parentParent->_left = subR; // 更新父节点的左子节点为右子节点  
        }  
        else  
        {  
            parentParent->_right = subR; // 如果原父节点是其父节点的右子节点,则更新父节点的右子节点为右子节点  
        }  
        subR->_parent = parentParent; // 更新右子节点的父节点指针  
    }  
}

右旋

  • 右旋操作与左旋操作相反,通常用于当某个节点的左子节点是红色时。右旋操作将左子节点提升为父节点,并将原父节点降为其右子节点。同时,原父节点的右子节点则成为新父节点的左子节点。

  
  void RotateR(Node* parent)  
{  
    Node* subL = parent->_left; // 左子节点  
    Node* subLR = subL->_right; // 左子节点的右子节点  
  
    parent->_left = subLR; // 原父节点的左子节点指向左子节点的右子节点  
    if (subLR)  
        subLR->_parent = parent; // 更新subLR的父节点指针  
  
    subL->_right = parent; // 左子节点的右子节点指向原父节点  
    parent->_parent = subL; // 更新原父节点的父节点指针  
  
    Node* parentParent = parent->_parent; // 原父节点的父节点  
  
    if (_root == parent) // 如果原父节点是根节点  
    {  
        _root = subL; // 更新根节点为左子节点  
        subL->_parent = nullptr; // 根节点的父节点指针置为null  
    }  
    else  
    {  
        if (parentParent->_left == parent) // 如果原父节点是其父节点的左子节点  
        {  
            parentParent->_left = subL; // 更新父节点的左子节点为左子节点  
        }  
        else  
        {  
            parentParent->_right = subL; // 如果原父节点是其父节点的右子节点,则更新父节点的右子节点为左子节点  
        }  
        subL->_parent = parentParent; // 更新左子节点的父节点指针  
    }  
}

自检函数的实现

  • Check函数

这个函数递归地检查红黑树的每个节点,确保满足红黑树的性质。

首先,它检查传入的节点是否为空(即已经到达了叶子节点)。如果是,它会比较从根节点到该叶子节点的黑色节点数量是否等于refVal(预期值)。如果不等,则打印错误信息并返回false。接着,它检查当前节点是否与其父节点连续为红色,如果是,则打印错误信息并返回false。如果当前节点是黑色,则增加blacknum的计数。最后,它递归地检查左子树和右子树。

  • IsBalance 函数

这个函数用于检查整棵红黑树是否平衡。

首先,它检查根节点是否为空或红色。如果根节点为空,则树是平衡的;如果根节点为红色,则树不平衡。接下来,它计算从根节点到最左侧叶子节点路径上的黑色节点数量,并将此值作为refVal(预期值)。然后,它调用Check函数,从根节点开始,检查整棵树是否满足红黑树的性质,特别是黑色节点数量的要求。

bool Check(Node* root, int blacknum, const int refVal)
  {
   if (root == nullptr)
   {
    //cout << balcknum << endl;
    if (blacknum != refVal)
    {
     cout << "存在黑色节点数量不相等的路径" << endl;
     return false;
    }

    return true;
   }

   if (root->_color == RED && root->_parent->_color == RED)
   {
    cout << "有连续的红色节点" << endl;

    return false;
   }

   if (root->_color == BLACK)
   {
    ++blacknum;
   }

   return Check(root->_left, blacknum, refVal)
    && Check(root->_right, blacknum, refVal);
  }
 public:
  bool IsBalance()
  {
   if (_root == nullptr)
    return true;

   if (_root->_color == RED)
    return false;

   //参考值
   int refVal = 0;
   Node* cur = _root;
   while (cur)
   {
    if (cur->_color == BLACK)
    {
     ++refVal;
    }

    cur = cur->_left;
   }

   int blacknum = 0;
   return Check(_root, blacknum, refVal);
  }

红黑树类

  • 使用KV模型

  • 私有成员为红黑树根节点指针

  • 包含插入函数、旋转函数、中序遍历、判断是否是红黑树等

template<class K, class V>
 class RBTree
 {
  typedef RBTreeNode<K, V> Node;
 public:
  bool Insert(const pair<K, V>& kv)
  {
   if (_root == nullptr)
   {
    _root = new Node(kv);
    _root->_color = 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->_color = RED;
   if (parent->_kv.first < kv.first)
   {
    parent->_right = cur;
    cur->_parent = parent;
   }
   else
   {
    parent->_left = cur;
    cur->_parent = parent;
   }

   while (parent && parent->_color == RED)
   {
    Node* grandfather = parent->_parent;
    if (parent == grandfather->_left)
    {

     Node* uncle = grandfather->_right;
     if (uncle && uncle->_color == RED)
     {

      parent->_color = uncle->_color = BLACK;
      grandfather->_color = RED;


      cur = grandfather;
      parent = cur->_parent;
     }
     else
     {
      if (cur == parent->_left)
      {

       RotateR(grandfather);
       parent->_color = BLACK;
       grandfather->_color = RED;
      }
      else
      {

       RotateL(parent);
       RotateL(grandfather);
       cur->_color = BLACK;
       grandfather->_color = RED;
      }

      break;
     }
    }
    else
    {

     Node* uncle = grandfather->_left;
     if (uncle && uncle->_color == RED)
     {

      parent->_color = uncle->_color = BLACK;
      grandfather->_color = RED;


      cur = grandfather;
     }
     else
     {
      if (cur == parent->_right)
      {
       RotateL(grandfather);
       parent->_color = BLACK;
       grandfather->_color = RED;
      }
      else
      {

       RotateR(parent);
       RotateL(grandfather);
       cur->_color = BLACK;
       grandfather->_color = RED;
      }

      break;
     }
    }
   }

   _root->_color = BLACK;

   return true;
  }

  void RotateL(Node* parent)
  {
   Node* subR = parent->_right;
   Node* subRL = subR->_left;

   parent->_right = subRL;
   subR->_left = parent;

   Node* parentParent = parent->_parent;

   parent->_parent = subR;
   if (subRL)
    subRL->_parent = parent;

   if (_root == parent)
   {
    _root = subR;
    subR->_parent = nullptr;
   }
   else
   {
    if (parentParent->_left == parent)
    {
     parentParent->_left = subR;
    }
    else
    {
     parentParent->_right = subR;
    }

    subR->_parent = parentParent;
   }
  }

  void RotateR(Node* parent)
  {
   Node* subL = parent->_left;
   Node* subLR = subL->_right;

   parent->_left = subLR;
   if (subLR)
    subLR->_parent = parent;

   Node* parentParent = parent->_parent;

   subL->_right = parent;
   parent->_parent = subL;

   if (_root == parent)
   {
    _root = subL;
    subL->_parent = nullptr;
   }
   else
   {
    if (parentParent->_left == parent)
    {
     parentParent->_left = subL;
    }
    else
    {
     parentParent->_right = subL;
    }

    subL->_parent = parentParent;
   }
  }

  void InOrder()
  {
   _InOrder(_root);
   cout << endl;
  }

  void _InOrder(Node* root)
  {
   if (root == nullptr)
    return;

   _InOrder(root->_left);
   cout << root->_kv.first << " ";
   _InOrder(root->_right);
  }

 private:
  // 根节点->当前节点这条路径的黑色节点的数量
  bool Check(Node* root, int blacknum, const int refVal)
  {
   if (root == nullptr)
   {
    //cout << balcknum << endl;
    if (blacknum != refVal)
    {
     cout << "存在黑色节点数量不相等的路径" << endl;
     return false;
    }

    return true;
   }

   if (root->_color == RED && root->_parent->_color == RED)
   {
    cout << "有连续的红色节点" << endl;

    return false;
   }

   if (root->_color == BLACK)
   {
    ++blacknum;
   }

   return Check(root->_left, blacknum, refVal)
    && Check(root->_right, blacknum, refVal);
  }
 public:
  bool IsBalance()
  {
   if (_root == nullptr)
    return true;

   if (_root->_color == RED)
    return false;

   //参考值
   int refVal = 0;
   Node* cur = _root;
   while (cur)
   {
    if (cur->_color == BLACK)
    {
     ++refVal;
    }

    cur = cur->_left;
   }

   int blacknum = 0;
   return Check(_root, blacknum, refVal);
  }

 private:
  Node* _root = nullptr;
 };
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1570821.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

详解protected访问限定符

1.同一个包中的同一类 package demo1;public class Test1 {protected int a 10;protected void b() {System.out.println("这是protected修饰的成员方法");}public static void main(String[] args) {Test1 test new Test1();System.out.println(test.a);test.b()…

燃气管网安全运行监测系统功能介绍

燃气管网&#xff0c;作为城市基础设施的重要组成部分&#xff0c;其安全运行直接关系到居民的生命财产安全和城市的稳定发展。然而&#xff0c;随着城市规模的不断扩大和燃气使用量的增加&#xff0c;燃气管网的安全运行面临着越来越大的挑战。为了应对这些挑战&#xff0c;燃…

华媒舍:3个科学指导,协助油管大V写下爆款文章

油管&#xff08;YouTube&#xff09;作为一个重要的视频分享平台&#xff0c;吸引了很多的观众和原创者。作为一位油管大V&#xff0c;你可能会一直在努力提升自己的文章质量以吸引更多的观众和订阅者。下面我们就为您提供三个科学指导&#xff0c;帮助自己写下更具有爆品发展…

可视化图表组件:仪表盘,监控数据关键指标信息,海量示例。

仪表盘组件&#xff08;Dashboard Component&#xff09;是一种常见的可视化设计组件&#xff0c;用于展示和监控数据的关键指标和信息。它通常以仪表盘的形式呈现&#xff0c;类似于汽车仪表盘&#xff0c;可以通过各种图表、指示器和控件来展示数据&#xff0c;并提供交互和操…

Linux安装JDK17等通用教程

一、查看Linux系统是否有自带的jdk 1、查看当前是否有jdk版本&#xff0c;命令如下&#xff1a; java -version2、检测jdk的安装包&#xff0c;命令如下&#xff1a; rpm -qa | grep java3、接着进行一个个删除包&#xff0c;命令如下&#xff1a; rpm -e --nodeps 包名4、…

数据结构day2--双向链表

双向链表: 即可以从头遍历到尾部和从尾部遍历到头部的链表&#xff0c;每个结点包括两个链域&#xff1a;前驱指针域和后继指针域&#xff0c;所以比起单向链表&#xff0c;其可以在任意一个结点访问前后两个结点 关于双向链表的一个完整步骤为&#xff1a; 创建一个表头结构…

基于SSM的邮票鉴赏系统(有报告)。Javaee项目。ssm项目。

演示视频&#xff1a; 基于SSM的邮票鉴赏系统&#xff08;有报告&#xff09;。Javaee项目。ssm项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;通过Spring Spri…

VBA数据库解决方案第九讲:把数据库的内容在工作表中显示

《VBA数据库解决方案》教程&#xff08;版权10090845&#xff09;是我推出的第二套教程&#xff0c;目前已经是第二版修订了。这套教程定位于中级&#xff0c;是学完字典后的另一个专题讲解。数据库是数据处理的利器&#xff0c;教程中详细介绍了利用ADO连接ACCDB和EXCEL的方法…

get请求搜索功能爬虫

<!--爬虫仅支持1.8版本的jdk--> <!-- 爬虫需要的依赖--> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.2</version> </dependency>…

Unity开发者3D模型基础

术语“3D 建模”是指使用特殊软件创建对象或表面的 3D 数字表示的过程。 3D 模型可用于各种不同的目的&#xff0c;包括电影、视频游戏、建筑和工程。 3D 建模也是创建虚拟现实 (VR) 和增强现实 (AR) 体验工作的重要组成部分。 我们通常通过构建或获取 3D 模型并将其导入 Unit…

2024/4/5 AT24C02 总线(I²C总线)

存储器的介绍&#xff1a; 一、易失性存储器RAM&#xff1a;存储速度快&#xff0c;掉电丢失 SRAM&#xff08;静态RAM&#xff09;&#xff1a;极快DRAM&#xff08;动态RAM&#xff09;&#xff1a;需要配一个扫描电路&#xff0c;进行“补电”&#xff08;动态刷新&#x…

【数据库】主流数据库及其常用工具简单科普

主流数据库及其常用工具 数据库分类关系型数据库&#xff08;RDBMS&#xff09;非关系型数据库&#xff08;NoSQL&#xff09;混合型数据库&#xff08;Hybrid Databases&#xff09;对象关系数据库&#xff08;ORDBMS&#xff09;多维数据库&#xff08;Multidimensional Data…

MySQL介绍和安装

MySQL介绍和安装 文章目录 MySQL介绍和安装1.MySQL介绍2.MySQL安装2.1 主机初始化2.1.1 设置网卡名和ip地址2.1.2 配置镜像源2.1.3 关闭防火墙2.1.4 禁用SELinux2.1.5 设置时区 2.2 包安装2.2.1 Rocky和CentOS 安装 MySQL2.2.2 Ubuntu 安装 MySQL 2.3 二进制安装安装MySQL2.3.1…

解决windows下Qt Creator显示界面过大的问题

&#x1f40c;博主主页&#xff1a;&#x1f40c;​倔强的大蜗牛&#x1f40c;​ &#x1f4da;专栏分类&#xff1a;QT❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 目录 问题描述 解决方法 1、右击此电脑--->属性 2、点击高级系统设置--->点击环境变量 3、 找到系…

Zookeeper学习二集群搭建

Zookeeper 集群介绍 Leader选举&#xff1a; Serverid&#xff1a;服务器ID 比如有三台服务器&#xff0c;编号分别是1,2,3。 编号越大在选择算法中的权重越大。 Zxid&#xff1a;数据ID 服务器中存放的最大数据ID.值越大说明数据 越新&#xff0c;在选举算法中数据…

【价格表】2024年统计大厂云服务器实时优惠活动,推荐最具性价比的云服务器,最便宜,华为云 京东云 阿里云 腾讯云低至50元/年

写作初衷&#xff1a; 作为一个购买多年云服务器经历的爱好者&#xff0c;最喜欢看各厂商的优惠活动&#xff0c;反复比较各厂商的优惠&#xff0c;找到最具性价比的那一款。 我就像一个互联网的小人物&#xff0c;在京东云、阿里云、腾讯云的官网里反复对比、反复横跳…

电子邮件的优点和缺点

没有任何一种通信方式能像电子邮件一样长期如此受欢迎。当你想到忙碌的职业人士在企业或办公室环境中工作时&#xff0c;你可能会想象他们正专心致志地给某人写邮件&#xff0c;按照指示传递信息。电子邮件的优点和缺点是什么&#xff1f;优点包括易于访问、透明度高&#xff0…

达梦disql登录数据库显示“未连接”

问题&#xff1a;达梦数据库在使用disql登录时&#xff0c;显示“未连接”。 指定了IP和端口号还是连接异常。 [dmdbatest ~]$ disql sysdba/Dameng123 disql V8 SQL> select * from v$instances; 未连接 SQL> exit [dmdbatest ~]$ disql sysdba/Dameng123localhost:52…

Vue使用高德地图(快速上手)

1.在高德平台注册账号 2.我的 > 管理管理中添加Key 3.安装依赖 npm i amap/amap-jsapi-loader --save 或 yarn add amap/amap-jsapi-loader --save 4.导入 AMapLoade import AMapLoader from amap/amap-jsapi-loader; 5.直接上代码&#xff0c;做好了注释&#xff08;初…

基于单片机的智能报站系统仿真设计

**单片机设计介绍&#xff0c;基于单片机的智能报站系统仿真设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机的智能报站系统仿真设计概要是关于采用单片机技术实现公交车报站功能的系统设计概述。以下是对该设计的…