C++ 学习日志 · 红黑树

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: C++ 学习日志 · 红黑树

红黑树:

红黑树 就和他的名字一样,树中只存在两种颜色的节点,他的本质也是一种二叉搜索树,它相对于使用平衡因子控制的二叉树,它会稍微宽松一些,控制平衡因子绝对值不超过1会比较严格也就意味着会有更多的旋转


红黑树的性质:

       ① 根节点是黑色

        ② 没有连续的红节点,红节点下一个节点必是黑节点

        ③ 每个节点的每条路径的都有相同数量的黑色节点

    ④ 每个叶子节点(空节点)都是黑色节点

综合以上的性质,可以得出新的性质:

       最长路径的节点个数不会超过最短路径的节点个数二倍

  最短路径:全是黑色节点

       最长路径:一黑一红交叉


模拟红黑树(三叉链):

insert:

红黑树和平衡二叉树相比,没有平衡二叉树那么严格,旋转的更少,但是既然要插入一个值,它的位置是可以根据子节点 左小右大 来确定,那它的颜色该如何选择呢?

       颜色的选择就要回归到红黑的性质(规则)上面

       如果插入的是红色节点,那么有可能破坏第②个规则

       如果插入的是黑色节点,那么就一定会破坏第③个规则,这样涉及新增节点的路径上的黑色

节点数量就会比其他的路径的黑色节点数量多一个

综上得出结论:插入的节点得是一个红色节点。 (毕竟这样我们还能"补救")

c6bfe2b5d76441cd9664932244e9921b.jpg

那大家看现在这种情况应该怎么调整颜色呢?

       调整颜色的同时也需要不破坏红黑树的规则:

       首先保证没有连续的红色节点,那么那么新节点的父亲节点就需要变成黑色,此时路径上的黑色节点的数量不统一,那么叔叔节点也应该变成黑色,

此时,最上面的爷爷节点,就要判断是否是根节点,如果是根节点则为黑色(规则第一条),不是则变为红色继续向上遍历   (如果此时爷爷不是根节点还保持黑色不变的话,那么图片中这一小的分支路径上面就会多出来一个节点)

e1065e4dfb6e4d1ea2c4c567e265abe0.jpg

bool insert(const pair<K, V>& data)
  {
    if (_root == nullptr)
    {
      _root = new node(data);
      _root->col = BLACK;
      return;
    }
    node* parent = nullptr;
    node* cur = _root;
    while (cur)
    {
      if (cur->_key > data)
      {
        parent = cur;
        cur = cur->_left;
      }
      else if (cur->_key < data)
      {
        parent = cur;
        cur = cur->_right;
      }
      else
      {
        return false;
      }
    }
    //插入新节点
    node* NewNode = new node(data);
    if (parent->_key > data)
    {
      parent->_left = NewNode;
    }
    else
    {
      parent->_right = NewNode;
    }
    cur = NewNode;
    while (parent && parent->col == RED)
    {
      node* groundnode = parent->_parent;
      if (groundnode->_left == parent)
      {
        node* unclenode = groundnode->_right;
        if (unclenode && unclenode == RED)
        {
          parent->col = unclenode->col = BLACK;
          groundnode->col = RED;
          cur = groundnode;
          parent = cur->_parent;
        }
      }
      else
      {
        node* unclenode = groundnode->_left;
      }
    }
  }

注意:颜色变化根子节点是父节点的左右没有关系

之前讨论的是叔叔节点存在且为黑的情况,那如果现在叔叔节点不存在呢

b5bbe071c5d54f32aeb440fa08dabbc5.jpg

这个时候就需要进行旋转了

2f8a4d21792c468486248b39417c831e.gif

注意:这个菱形代表的子节点的多种情况


现在如果叔叔节点,存在并且是黑色的呢?

4fb2d2c4a5124d98b606d7c0f306216a.jpg大家看这张图,就能明白有的时候一种情况,多数是由其他情况在调整的时候演变出来的上图就是叔叔节点存在且为黑的情况,那么此时应该怎么做呢?

c34d4cc799a64b90931d0df344ec90c9.gif

a462542fe31a46349eaeb1d80023f8ec.jpg

当然这个是右单旋(儿子节点、父亲节点它俩都是各自父亲节点的左节点),左单选的原理和这个是一样的.

bool insert(const pair<K, V>& data)
  {
    if (_root == nullptr)
    {
      _root = new node(data);
      _root->col = BLACK;
      return;
    }
    node* parent = nullptr;
    node* cur = _root;
    while (cur)
    {
      if (cur->_key > data)
      {
        parent = cur;
        cur = cur->_left;
      }
      else if (cur->_key < data)
      {
        parent = cur;
        cur = cur->_right;
      }
      else
      {
        return false;
      }
    }
    //插入新节点
    node* NewNode = new node(data);
    if (parent->_key > data)
    {
      parent->_left = NewNode;
    }
    else
    {
      parent->_right = NewNode;
    }
    cur = NewNode;
    while (parent && parent->col == RED)
    {
      node* groundnode = parent->_parent;
      if (groundnode->_left == parent)
      {
        node* unclenode = groundnode->_right;
        if (unclenode && unclenode == RED)
        {
          parent->col = unclenode->col = BLACK;
          groundnode->col = RED;
          cur = groundnode;
          parent = cur->_parent;
        }
        else
        {
          if (parent->_left == cur)
          {
            RotateR(groundnode);
            parent->col = BLACK;
            groundnode->col = RED;
          }
          else
          {
            RtotateL(parent);
            RtotateR(groundnode);
            cur->col = BLACK;
            groundnode->col = RED;
          }
          break;
        }
      }
      else
      {
        node* unclenode = groundnode->_left;
        if (unclenode && unclenode == RED)
        {
          parent->col = unclenode->col = BLACK;
          groundnode->col = RED;
          cur = groundnode;
          parent = cur->_parent;
        }
        else
        {
          if (parent->_right == cur)
          {
            RotateL(groundnode);
            parent->col = BLACK;
            groundnode->col = RED;
          }
          else
          {
            RtotateR(parent);
            RtotateL(groundnode);
            cur->col = BLACK;
            groundnode->col = RED;
          }
          break;
        }
      }
    }
  }


相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
22天前
|
编译器 C语言 C++
配置C++的学习环境
【10月更文挑战第18天】如果想要学习C++语言,那就需要配置必要的环境和相关的软件,才可以帮助自己更好的掌握语法知识。 一、本地环境设置 如果您想要设置 C++ 语言环境,您需要确保电脑上有以下两款可用的软件,文本编辑器和 C++ 编译器。 二、文本编辑器 通过编辑器创建的文件通常称为源文件,源文件包含程序源代码。 C++ 程序的源文件通常使用扩展名 .cpp、.cp 或 .c。 在开始编程之前,请确保您有一个文本编辑器,且有足够的经验来编写一个计算机程序,然后把它保存在一个文件中,编译并执行它。 Visual Studio Code:虽然它是一个通用的文本编辑器,但它有很多插
|
1月前
|
Arthas 监控 Java
JVM知识体系学习七:了解JVM常用命令行参数、GC日志详解、调优三大方面(JVM规划和预调优、优化JVM环境、JVM运行出现的各种问题)、Arthas
这篇文章全面介绍了JVM的命令行参数、GC日志分析以及性能调优的各个方面,包括监控工具使用和实际案例分析。
43 3
|
1月前
|
存储 Prometheus NoSQL
大数据-44 Redis 慢查询日志 监视器 慢查询测试学习
大数据-44 Redis 慢查询日志 监视器 慢查询测试学习
24 3
|
1月前
|
Java 编译器 C++
c++学习,和友元函数
本文讨论了C++中的友元函数、继承规则、运算符重载以及内存管理的重要性,并提到了指针在C++中的强大功能和使用时需要注意的问题。
19 1
|
1月前
|
数据采集 监控 Java
SpringBoot日志全方位超详细手把手教程,零基础可学习 日志如何配置及SLF4J的使用......
本文是关于SpringBoot日志的详细教程,涵盖日志的定义、用途、SLF4J框架的使用、日志级别、持久化、文件分割及格式配置等内容。
127 0
SpringBoot日志全方位超详细手把手教程,零基础可学习 日志如何配置及SLF4J的使用......
|
1月前
|
Python
log日志学习
【10月更文挑战第9天】 python处理log打印模块log的使用和介绍
30 0
|
1月前
|
缓存 Linux 编译器
【C++】CentOS环境搭建-安装log4cplus日志组件包及报错解决方案
通过上述步骤,您应该能够在CentOS环境中成功安装并使用log4cplus日志组件。面对任何安装或使用过程中出现的问题,仔细检查错误信息,对照提供的解决方案进行调整,通常都能找到合适的解决之道。log4cplus的强大功能将为您的项目提供灵活、高效的日志管理方案,助力软件开发与维护。
54 0
|
2月前
|
存储 运维 监控
超级好用的C++实用库之日志类
超级好用的C++实用库之日志类
40 0
|
6天前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
29 4
|
7天前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
25 4