算法实战:亲自写红黑树之一

news/2024/5/20 2:15:08

初级代码游戏的专栏介绍与文章目录-CSDN博客

我的github:codetoys,所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。

这些代码大部分以Linux为目标但部分代码是纯C++的,可以在任何平台上使用。


        写了一个红黑树算法,有一点心得跟大家交流一下。 

        (大约是2023年底写的)

目录

一、缘起

二、过程

三、红黑树的理解

3.1 红黑计算

3.2 叶子节点NIL

3.3 删除产生“双黑”是什么意思

3.4 为什么删除只考虑有一个子结点的情况

四、编程经验

4.1 底层抽象

4.2 辅助代码

五、算法


一、缘起

        上周一摸鱼闲聊,想起来我的代码库里有个核心算法是别人写的,是树的平衡,具体什么算法不清楚。我一直有想法把这个代码替换掉,替换掉后我的代码库就全部都是我自己写的了,于是就让别人看了看,别人说这是平衡二叉树,不是红黑树,红黑树才是综合性能最好的。

        当初为什么这个算法给别人写了呢?因为当时是在项目中,时间比较紧,没时间去写一个心里没谱的复杂算法,于是就用一个简单折中办法:每次插入时计算深度,深度超过理论平衡二叉树深度的两倍就完全排序重建树。

        旁边一个兄弟说他有兴趣挑战一下这个算法,于是他花了大约两周完成了这个算法(由于在大数据量测试中不断发生问题,所以大约两周后才最终测试通过)。后来就一直用他写的这个算法,我也没在意用的是平衡二叉树还是红黑树,它们的性能差异不是很重要。

        有人说各种类库都有现成算法,用的还都是红黑树,为什么我的要自己写呢?这真不是为了练手,而是因为我们用的是共享内存,不能基于指针,而我实在找不到通过包装来利用stl算法的方法,所以不得不自己写。

        当然了,我并不认为“重复发明轮子”是不必要的行为,这是另一个话题,不展开讲了。

二、过程

        下了决心要写红黑树,就上网搜罗了各种文章来学习,理解了一下各种概念,发现红黑树的几个定义大家说的当然都一样,阐述却不是很一样,各人角度各有不同,我的理解也不太一样,所以我会把我的理解方式写出来跟大家探讨。

        写插入算法挺简单,随便哪个文章照着写就对了。很容易就测试通过了。由于树的基础功能是以前就写好的,所以只花了半天就把插入算法写好了。

        写删除算法就麻烦了,由于写的时候对算法的理解还不够透彻,不断测试不断错,不断修改,最后发现是因为之前对别人文章的理解错了(而不是别人的文章错了)。写删除算法花了整整两天(程序员的两天,每天不只是八小时)。

三、红黑树的理解

3.1 红黑计算

        红节点不计数,黑节点算深度,也就可以理解为,红节点就是相比平衡二叉树多出来的节点。

        因为红节点不允许连续,这就限制了红节点的数量,大致不能超过黑节点数太多(不做论文的话不用太精确)。

        新增节点首先设置为红色,遇到黑色父节点不用平衡,遇到红色父节点再做平衡。这就能减少一部分平衡操作(具体减少多少不好说),所以比平衡二叉树综合性能高一些。

        因为规定从根到叶子节点(NIL节点)的黑节点数都相同,所以扣除红节点,剩下的树看起来是类似平衡树的。

3.2 叶子节点NIL

        红黑树所有叶子节点都是NIL,就是把左右子树为空的空指针当作叶子,因为最终每条分支都一定会以NIL分支结束,而且NIL规定为黑色,所以这个所谓的“叶子”不影响红黑计算。

        根节点规定为黑色,也不影响红黑计算。

3.3 删除产生“双黑”是什么意思

        很多文章描述删除时使用“双黑节点”这个概念,这个概念其实就是“这个节点的黑高少一”。

        这种情形由删除了一个黑色数据节点形成,因为删除了一个,当然黑高减少1。

        “双黑变单黑”这个说法我就不是很能理解。我觉得理解成“黑高少一”就可以了。

        解决黑高少一的办法不外乎两个:

        1,从兄弟节点那边挪一个红色节点过来变成黑色,黑高恢复

        2,把兄弟节点那边变一个红色出来,兄弟节点也黑高减一,两边平衡,父节点变成了黑高少一节点,向上递归处理即可

        后面我会详细解释删除算法。

3.4 为什么删除只考虑有一个子结点的情况

        这里说的子节点指的是有数据的子节点,不是NIL叶子节点。

        因为删除中间节点可以通过用左子树最大值或右子树最小值交换的方法把问题转换为只有一个子结点的情况。

四、编程经验

4.1 底层抽象

        编写算法一定要做底层抽象,这样算法才能复用。因为我一直在共享内存上写东西,所以对STL直接使用指针这个事挺抓狂。指针作为基础类型,是不可以重定义的。

        因为我这里数据都是通过索引访问的,所以我用这种方式来访问实际数据:

struct TREE_NODE
{static TREE_NODE& at(T_SIZE n);T_SIZE _me()const;
};

        静态方法at(T_SIZE n)获取n处的数据,至于如何获取,可以很简单,也可以很复杂。

        _me()返回自己的位置。对于指针,n就是自己,自己就是n,但是这里n不是指针,所以就有了反向查找n的需要。

4.2 辅助代码

        编写算法没有辅助代码根本不行。编写过程中我写了图形化显示树的代码和检测树结构的代码,以及测试验证的代码。

        测试验证的代码必须是自动化一次完成的,遇到错误自动停止。为了跟踪错误,要有很多日志输出,但是大数据量测试会产生过多输出,所以我用了一个小技巧:

        先用关闭调试执行,遇到错误再打开调试重新执行:

int main()
{G_IS_DEBUG = false;//关闭调试输出int count = 1000;//每次测试的数据量for (int i = 0; i < 100; ++i){if (!test(i, count))//以不同的随机数种子生成数据测试{G_IS_DEBUG = true;//遇到错误,打开调试,重新执行出错的参数test(i, count);thelog << "失败 i= " << i << endi;return 1;}thelog << TimeToString_log(time(NULL)) << " " << i << endi;}return 0;
}

五、算法

        算法实战:亲自写红黑树之二 完整代码-CSDN博客

        算法实战:亲自写红黑树之三 算法详解-CSDN博客

        算法实战:亲自写红黑树之四 插入insert的平衡-CSDN博客

        算法实战:亲自写红黑树之五 删除erase的平衡-CSDN博客


(这里是结束)


http://www.mrgr.cn/p/48163181

相关文章

网络安全之ACL

ACL&#xff1a;访问控制列表——控制列表&#xff08;策略列表&#xff09;&#xff0c;是一个控制工具。 功能&#xff1a;&#xff01;、定义感兴趣路由&#xff08;控制层面&#xff09;。2、定义感兴趣流量&#xff08;数据层面&#xff09;。 例如&#xff1a; 假设在该…

【MsSQL】数据库基础 库的基本操作

目录 一&#xff0c;数据库基础 1&#xff0c;什么是数据库 2&#xff0c;主流的数据库 3&#xff0c;连接服务器 4&#xff0c;服务器&#xff0c;数据库&#xff0c;表关系 5&#xff0c;使用案例 二&#xff0c;库的操作 1&#xff0c;创建数据库 2&#xff0c;创建…

【攻防技术系列+Python】-- 用 Python 控制系统进程

用 Python 控制系统进程 由于注册表几乎可以决定整个操作系统的运行,因此它成为安全工具与恶意软件对抗的主要战场之一。除了注册表之外,对系统进程的控制也是安全工具和恶意软件的必争之地。这里我们首先要了解程序和进程的区别。程序是静态的,进程是动态的。进程可以分为系…

两个手机在一起ip地址一样吗?两个手机是不是两个ip地址

在数字时代的浩瀚海洋中&#xff0c;手机已经成为我们生活中不可或缺的一部分。随着移动互联网的飞速发展&#xff0c;IP地址成为了连接手机与互联网的桥梁。那么&#xff0c;两个手机在一起IP地址一样吗&#xff1f;两个手机是不是两个IP地址&#xff1f;本文将带您一探究竟&a…

【快速入门Linux】10_Linux命令—Vi编辑器

文章目录 一、vi 简介1.1 vi1.2 vim1.3查询软连接命令&#xff08;知道&#xff09; 二、打开和新建文件&#xff08;重点&#xff09;2.1 打开文件并且定位行2.2 异常处理 三、vi三种工作模式&#xff08;重点&#xff09;3.1 末行模式-命令 四、常用命令4.0 命令线路图4.1 移…

图像涂哪就动哪!Gen-2新功能“神笔马良”爆火,网友:急急急

AI搞视频生成&#xff0c;已经进化到这个程度了&#xff1f;&#xff01; 对着一张照片随手一刷&#xff0c;就能让被选中的目标动起来&#xff01; 明明是一辆静止的卡车&#xff0c;一刷就跑了起来&#xff0c;连光影都完美还原&#xff1a; 原本只是一张火灾照片&#xff0…

STM32快速入门(串口传输之USART)

STM32快速入门&#xff08;串口传输之USART&#xff09; 前言 USART串口传输能实现信息在设备之间的点对点传输&#xff0c;支持单工、半双工、全全双工&#xff0c;一般是有三个引脚&#xff1a;TX、RX、SW_RX&#xff08;共地&#xff09;。不需要一根线来同步时钟。最大优…

Hadoop3:集群搭建及常用命令与shell脚本整理(入门篇,从零开始搭建)

一、集群环境说明 1、用VMware安装3台Centos7.9虚拟机 2、虚拟机配置&#xff1a;2C&#xff0c;2G内存&#xff0c;50G存储 3、集群架构设计 从表格中&#xff0c;可以看出&#xff0c;Hadoop集群&#xff0c;主要有2个模块服务&#xff0c;一个是HDFS服务&#xff0c;一个是…

基于web的物流管理系统

文章目录 项目介绍主要功能截图&#xff1a;部分代码展示设计总结项目获取方式 &#x1f345; 作者主页&#xff1a;超级无敌暴龙战士塔塔开 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、 简历模板、学习资料、面试题库【关注我&#xff0c;都给你】 &…

特斯拉CEO马斯克访华,或加速FSD技术在中国的落地

特斯拉首席执行官埃隆马斯克于4月底进行了中国之旅&#xff0c;这一访问被业内人士认为可能加速特斯拉FSD&#xff08;Full Self-Drive&#xff0c;完全自动驾驶&#xff09;技术在中国的应用。业内专家指出&#xff0c;马斯克的此番到访可能会对中国自动驾驶市场产生深远影响&…

VMware如何将虚拟机的端口服务映射出去

我们有时候在VMware起了一个服务,想要局域网的朋友同事访问 这时候就需要i端口映射 选择NAT模式 VMnet8点击 NAT设置 然后点击添加然后映射传入端口对话框 红色部分是 你主机本机,也就是你在用的电脑的空闲端口(可以打开cmd 输入命令 : netstat -ano 查看已用端口都有哪些…

c++多线程2小时速成

简介 c多线程基础需要掌握这三个标准库的使用&#xff1a;std::thread,std::mutex, andstd::async。 1. Hello, world #include <iostream> #include <thread>void hello() { std::cout << "Hello Concurrent World!\n"; }int main() {std::th…

CSS 伪类、伪元素的应用实例:电池充电、高能进度条

一、目的 本文通过 CSS 伪类、伪元素&#xff0c;结合动画 animation 和 Vue 动态样式属性&#xff08;通过 CSS 变量&#xff09;的写法&#xff0c;来实现电池充电、高能进度条的效果&#xff0c;如下图所示。 二、基础知识 1、CSS 伪类、伪元素 简单概括成以下 4 点&#x…

亚马逊Amazon商品详情和关键词搜索API接口分享

一、亚马逊Amazon商品详情API接口 亚马逊商品详情API接口是亚马逊平台为开发者提供的一项重要服务&#xff0c;它允许开发者通过程序调用API来获取亚马逊商品的相关数据。这个接口为获取商品数据提供了便利的途径&#xff0c;有助于用户进行商品搜索、商品分类以及数据分析等操…

基于改进MFCC特征和卷积递归神经网络的心音分类

具体的软硬件实现点击http://mcu-ai.com/MCU-AI技术网页_MCU-AI人工智能 心音分类在心血管疾病的早期发现中起着至关重要的作用,特别是对于小型初级卫生保健诊所。尽管近年来心音分类取得了很大进展,但其中大多数都是基于传统的分段特征和基于浅层结构的分类器。这些传统的声…

2024最详细全面的发卡平台对比调研

最近在调研目前市面上的发卡平台&#xff0c;对一些主流的托管式发卡平台与github上开源的发卡项目做了横向对比&#xff0c;本文主要介绍各自特点以及需要注意避免的坑。 直接上表格&#xff0c;一目了然。 对比独角数卡***发卡/泛发卡平台iDataRiver发卡稳定性/跑路风险自己…

QT day2 作业

头文件 #ifndef MYWIDGET_H #define MYWIDGET_H#include <QWidget> #include <QDebug> #include<QIcon> #include<QLabel> #include<QMovie> #include<QLineEdit> #include<QPushButton> QT_BEGIN_NAMESPACE namespace Ui { class …

ansible------inventory 主机清单

目录 inventory 中的变量 2&#xff09;组变量[webservers:vars] #表示为 webservers 组内所有主机定义变量&#xff0c;所有组内成 员都有效 ansible_userrootansible_passwordabc1234 3&#xff09; [all:vars…

Golang | Leetcode Golang题解之第71题简化路径

题目&#xff1a; 题解&#xff1a; func simplifyPath(path string) string {stack : []string{}for _, name : range strings.Split(path, "/") {if name ".." {if len(stack) > 0 {stack stack[:len(stack)-1]}} else if name ! "" &am…

Redis线程模型

文章目录 &#x1f496; Redis 单线程模型⭐ 单线程监听大量的客户端连接⭐ Redis 6.0 之前为什么不用多线程&#xff1f; &#x1f496; Redis多线程⭐ Redis 后台线程⭐ Redis 网络IO多线程 对于读写命令来说&#xff0c;Redis 一直是单线程模型。不过&#xff0c;在 Redis 4…