红黑树作业

news2024/11/15 16:56:29

简介

本文是为操作系统作业而实现的红黑树源码。作业题目选题为:“(3)红黑树(rbtree)数据结构介绍及其在Linux内核中的应用(结合内核源码进行分析,内核版本号不低于2.6.0)”。

因为网上太多红黑树原理的讲解了,不需要再去讲解原理,所以在这就提供一些讲原理不错的博客,然后主要是给红黑树代码的实现(链接:https://pan.baidu.com/s/15clWdClNPnHA3bd-2FAcqQ 提取码:1234 ),用的c++语言模板实现红黑树,并附带详细注释。(目前只实现了红黑树的插入与查询操作,并可视化打印红黑树)。

红黑树插入操作的流程图:(参考:红黑树总结 - lukazan - 博客园 (cnblogs.com),该博客用图详细解释了红黑树插入操作的事件,然后我用红色对其事件进行了编号,以便于和代码注释相对应)

编辑

红黑树的本质其实就是2-3树,在上面推荐的博客中也有提到。

代码实现

目录结构:

编辑

build 为构建目录

inlcude 为头文件目录(因为用到了c++模板,将类的定义与实现都写入该hpp文件中,这样比较方便,如果打算分开写到头文件.h与源文件.cpp中也是可以的,不过需要加上 #include"xxx.cpp",具体原因自行百度)

src 为源代码目录,在这里就是测试使用红黑树类的源文件

CMakeLists.txt:

cmake_minimum_required(VERSION 3.0)

project(RedBlackTree)

include_directories(${CMAKE_SOURCE_DIR}/include)

aux_source_directory(${CMAKE_SOURCE_DIR}/include/ Sources)

add_executable(main ${CMAKE_SOURCE_DIR}/src/main.cpp ${Sources})

include--RBTreeNode.hpp:

#ifndef _RBTREENODE_HPP_#define _RBTREENODE_HPP_enumColor// 1 is red,0 is black node
{
    Black = 0, 
    Red
};


template<typename T> 
classRBTreeNode
{

public :
    T data;
    Color color; // 1 代表红色, 0 代表黑色
    RBTreeNode* father;  // 父节点
    RBTreeNode* left;    // 左孩子
    RBTreeNode* right;   // 右孩子public :
    RBTreeNode(); 
    voidRBTreeNodeSet(T data); // 配合默认构造函数使用,使用默认构造函数没有初始值,但每个节点必须包含值,所以该函数可以另外设置初始值或者修改初始值RBTreeNode(T data);
    ~RBTreeNode();    
};

template<typename T>
RBTreeNode<T>::RBTreeNode() : color(Red), father(nullptr), left(nullptr), right(nullptr)
{

}

template<typename T>
RBTreeNode<T>::RBTreeNode(T data) : data(data), color(Red) , father(nullptr), left(nullptr), right(nullptr)
{

}

template<typename T>
void RBTreeNode<T>::RBTreeNodeSet(T data)
{
    this->data = data;
}

template<typename T>
RBTreeNode<T>::~RBTreeNode()
{
    this->father = nullptr;
    this->left = nullptr;
    this->right = nullptr;
}

#endif

include--RBTree.hpp:

#ifndef _RBTREE_HPP_#define _RBTREE_HPP_#include"RBTreeNode.hpp"#include<iostream>usingnamespace std;

template<typename T>
voidFreeTree(RBTreeNode<T>* curNode);

template<typename T>
classRBTree
{

public :
    RBTreeNode<T>* treeRoot;

public :
    RBTree();
    RBTree(RBTreeNode<T>* node);
    ~RBTree();

    friendvoidFreeTree(RBTreeNode<T>* curNode); // 释放红黑树的内存boolInsert(T data); // 插入节点boolRightRotate(RBTreeNode<T>* curNode); // 右旋boolLeftRotate(RBTreeNode<T>* curNode); // 左旋boolGet(T data); // 查看数据是否存在voidViewTree(RBTreeNode<T>* curNode); // 查看树的结构voidOutput(RBTreeNode<T>* curNode, bool left, string const& indent); // 可视化红黑树voidViewTreeByGraphic(RBTreeNode<T>* curNode); // 可视化红黑树
};

template<typename T>
RBTree<T>::RBTree() : treeRoot(nullptr)
{

}

template<typename T>
RBTree<T>::RBTree(RBTreeNode<T>* node) : treeRoot(node) {}

template<typename T>
RBTree<T>::~RBTree()
{
    FreeTree(treeRoot);
}

template<typename T>
voidFreeTree(RBTreeNode<T>* curNode){
    if(curNode->right) FreeTree(curNode->right);
    if(curNode->left)  FreeTree(curNode->left);

    if(curNode != nullptr) // 回溯删除所有节点,释放堆上的内存
    {
        // cout << "This node is deleted : " << curNode->data << '\n';delete curNode;
        curNode = nullptr;
        return ;
    }
}

template<typename T>
bool RBTree<T>::Insert(T data)
{
    if(treeRoot == nullptr) // 1. 红黑树为空树的情况,将节点作为根节点,并且设置为黑色
    {
        treeRoot = newRBTreeNode<T>(data);
        treeRoot->color = Black;
        return1;
    }

    RBTreeNode<T>* tmpTreeRoot = treeRoot;
    RBTreeNode<T>* curNode = newRBTreeNode<T>(data); // 当前需要插入的节点int flag = -1; // 标记是插入左边还是右边或者是值相等,左边用1标记,右边用2标记,相等用3标记。while(tmpTreeRoot != nullptr) // 查找需要插入节点的父节点
    {
        if(curNode->data < tmpTreeRoot->data) // 在当前节点的左边
        {
            if(tmpTreeRoot->left != nullptr) tmpTreeRoot = tmpTreeRoot->left; // 往左走else{
                flag = 1; // 左边break; // 如果左边是空的,那么该空的位置就应该插入新的节点,所以跳出
            }
        }
        elseif(curNode->data > tmpTreeRoot->data)
        {
            if(tmpTreeRoot->right != nullptr) tmpTreeRoot = tmpTreeRoot->right; // 往右走else{
                flag = 2; // 右边break;
            }
        }
        else
        {
            tmpTreeRoot->data = curNode->data;
            flag = 3; // 相等break;
        }
    }

    // 查看插入时父节点的相关数据// cout << "Insert data = " << data << " flag = " << flag << " father data = " << tmpTreeRoot->data << " color = " << tmpTreeRoot->color << '\n';// 目前 tmpTreeRoot 为需要插入的节点的父节点if(flag == 3) return1;  // 2. 插入点为Key已存在的情况if(flag == 1) // 插入节点
    {
        tmpTreeRoot->left = curNode;
        curNode->father = tmpTreeRoot;
    }
    elseif(flag == 2)
    {
        tmpTreeRoot->right = curNode;
        curNode->father = tmpTreeRoot;
    }


    if(tmpTreeRoot->color == Black) return1; // 3. 如果父节点为黑色,返回// cout << "hello\n";// 4. 插入节点的父节点为红色,需要修复

    RBTreeNode<T>* uncleNode = nullptr;

    while(  curNode->father != nullptr && // 当前节点不是根节点
            curNode->father->father != nullptr && // 当前节点有爷爷节点
            curNode->father->color == Red) // 当前节点的父节点是红色节点
    {

//        grandfather//            / \ //           /   \ //      father    ?(4.1)//        / \ //(4.2.1)?   ?(4.2.2)

        RBTreeNode<T>* fatherNode = curNode->father;
        RBTreeNode<T>* grandFatherNode = fatherNode->father;

        if(fatherNode == grandFatherNode->left) // 4.2.1 父节点为左子树
        {
            uncleNode = grandFatherNode->right;
            if(uncleNode != nullptr && uncleNode->color == Red) // 4.1 叔叔节点存在且为红色
            {
                fatherNode->color = Black;
                uncleNode->color = Black;
                if(grandFatherNode->father != nullptr) grandFatherNode->color = Red; // 如果为空那么该节点是父节点,不能是红色
                curNode = grandFatherNode;
            }
            else// 4.2 叔叔节点为空节点或者为黑色节点
            {
                if(curNode == fatherNode->left) // 4.2.1.1 当前节点在父节点的左边,LL(双红的情况)// 1. 将父节点设置为黑色,将爷爷节点设置为红色// 2. 对爷爷节点进行右旋
                {
                    fatherNode->color = Black;
                    grandFatherNode->color = Red;
                    RightRotate(grandFatherNode); // 右旋
                }
                elseif(curNode == fatherNode->right) // 4.2.1.2 当前节点的父节点的右边,LR双红
                {
                    curNode = curNode->father;
                    LeftRotate(curNode);
                }
            }
        }
        
//        grandfather//            / \ //           /   \ //      ?(4.1)   father//                / \ //        (4.2.1)?   ?(4.2.2)else// 4.2.2 父节点为右子树
        {
            uncleNode = grandFatherNode->left;
            if(uncleNode != nullptr && uncleNode->color == Red) 
// 4.1 叔叔节点存在,且为红色。// 叔叔节点的判断位置有些乱,是因为理论要先能找到叔叔节点,// 所以需要先进行4.2.1/2节的判断,也就是插入节点的父节点是爷爷节点的左孩子还是右孩子
            {
                fatherNode->color = Black;
                uncleNode->color = Black;
                if(grandFatherNode->father != nullptr) grandFatherNode->color = Red;
                curNode = grandFatherNode;
            }
            else
            {
                if(curNode == fatherNode->right) // 4.2.2.1 RR 双红
                {
                    fatherNode->color = Black;
                    grandFatherNode->color = Red;
                    LeftRotate(grandFatherNode);
                }
                elseif(curNode == fatherNode->left)// 4.2.2.2 RL双红
                {
                    curNode = curNode->father;
                    RightRotate(curNode);
                }
            }
        }
    }
    return1;
}

template<typename T>
bool RBTree<T>::RightRotate(RBTreeNode<T>* curNode)
{
    RBTreeNode<T>* curNodeLeft = curNode->left;
    RBTreeNode<T>* curNodeFather = curNode->father;

    // 操作1 :把当前节点的左孩子换成当前节点的左节点的右孩子
    curNode->left = curNodeLeft->right;
    if(curNode->left != nullptr) curNode->left->father = curNode;  // 判断当前节点的左节点的右孩子是否为空// 操作2 :把当前节点的作为当前节点的左节点的右孩子
    curNodeLeft->right = curNode;
    curNode->father = curNodeLeft;

    // 操作3 :把当前节点的父节点与当前节点的左节点进行连接if(curNodeFather == nullptr) // curNodeLeft为根节点
    {
        curNodeLeft->father = curNodeFather;
        treeRoot = curNodeLeft; // 这里需要注意,需要把根节点更新
    }
    elseif(curNodeFather->left == curNode)
    {
        curNodeFather->left = curNodeLeft;
        curNodeLeft->father = curNodeFather;
    }
    else
    {
        curNodeFather->right = curNodeLeft;
        curNodeLeft->father = curNodeFather;
    }

    return1;
}

template<typename T>
bool RBTree<T>::LeftRotate(RBTreeNode<T>* curNode)
{
    RBTreeNode<T>* curNodeRight = curNode->right;
    RBTreeNode<T>* curNodeFather = curNode->father;

    // 操作1 :把当前节点的右孩子换成当前节点的右节点的左孩子
    curNode->right = curNodeRight->left;
    if(curNode->right != nullptr) curNode->right->father = curNode;

    // 操作2 :把当前节点的作为当前节点的右节点的左孩子
    curNodeRight->left = curNode;
    curNode->father = curNodeRight;

    // 操作3 :把当前节点的父节点与当前节点的右节点进行连接if(curNodeFather == nullptr) // curNodeRight 为根节点
    {
        curNodeRight->father = curNodeFather;
        treeRoot = curNodeRight; // 更新根节点// cout << "root data = " << treeRoot->data << '\n';
    }
    elseif(curNodeFather->right == curNode) // 当前节点为当前节点父节点的右孩子
    {
        curNodeFather->right = curNodeRight;
        curNodeRight->father = curNodeFather;
    }
    else// 当前节点为当前节点父节点的左孩子
    {
        curNodeFather->left = curNodeRight;
        curNodeRight->father = curNodeFather;
    }
}

template<typename T>
bool RBTree<T>::Get(T data)
{
    RBTreeNode<T>* tmpRoot = treeRoot;
    while(tmpRoot != nullptr)
    {
        if(data < tmpRoot->data) // 往左
        {
            if(tmpRoot->left != nullptr) tmpRoot = tmpRoot->left;
            elsebreak;
        }
        elseif(data > tmpRoot->data) // 往右
        {
            if(tmpRoot->right != nullptr) tmpRoot = tmpRoot->right;
            elsebreak;
        }
        else// 找到了
        {
            return1;
        }
    } 
    return0; // 未找到
}

template<typename T>
void RBTree<T>::ViewTree(RBTreeNode<T>* curNode) // 线序遍历打印红色树查看正确性
{
    cout << curNode->data << ' ';
    if(curNode->left != nullptr) ViewTree(curNode->left);
    if(curNode->right != nullptr) ViewTree(curNode->right);
}

template<typename T>
void RBTree<T>::Output(RBTreeNode<T>* curNode, bool left, string const& indent)
{
    if (curNode->right)
    {
        Output(curNode->right, false, indent + (left ? "|      " : "       "));
    }
    cout << indent;
    cout << (left ? '\\' : '/');
    cout << "-----";
    cout << curNode->data << "(" << (curNode->color == Red ? 'R' : 'B') << ")" << '\n';
    if(curNode->left)
    {
        Output(curNode->left, true, indent + (left ? "       " : "|      "));
    }
    
}

template<typename T>
void RBTree<T>::ViewTreeByGraphic(RBTreeNode<T>* curNode)
{
    cout << "\n\n-------------------- Red Black Tree ------------------------";
    cout << "\n--------------- B : Black Node R : Red Node -------------------\n\n\n";
    if(curNode->right)
    {
        Output(curNode->right, false, "");
    }
    cout << curNode->data <<  "(" << (curNode->color == Red ? 'R' : 'B') << ")" <<  '\n';
    if(curNode->left)
    {
        Output(curNode->left, true, "");
    }
    cout << "\n\n------------------------------------------------------------\n";
}

#endif

src--main.cpp

#include<iostream>#include<cstring>#include<sstream>#include"RBTree.hpp"#include"RBTreeNode.hpp"usingnamespace std;

int a[30] = {0, 12, 1, 9, 2, 0, 11, 7, 19, 4, 15, 18, 5, 14, 13, 10, 16, 6, 3, 8, 17};

intmain(){
    RBTree<int> Tree;
    cout << "\nOrder of inserting of all nodes.\n\n";
    for(int i = 1; i <= 20; i++ )
    {
        cout << a[i] << ' ';
        Tree.Insert(a[i]); // 插入节点
    }
    cout << '\n';
    cout << "\nPre-order traversal of red-black tree : \n\n";
    Tree.ViewTree(Tree.treeRoot); // 红黑树的线序遍历
    Tree.ViewTreeByGraphic(Tree.treeRoot); // 可视化红黑树// for(int i = 1; i <= 20; i++ )// {//     cout << i << " : " << Tree.Get(a[i]) << '\n';// }
}

Result:

PS C:\CodeWork\Project\DataStructure\RBTree\build> .\main.exe

Order of inserting of all nodes.

12 1 9 2 0 11 7 19 4 15 18 5 14 13 10 16 6 3 8 17 

Pre-order traversal of red-black tree :

9 4 1 0 2 3 6 5 7 8 14 12 11 10 13 18 16 15 17 19

-------------------- Red Black Tree ------------------------
--------------- B : Black Node R : Red Node -------------------


              /-----19(B)
       /-----18(R)
       |      |      /-----17(R)
       |      \-----16(B)
       |             \-----15(R)
/-----14(B)
|      |      /-----13(B)
|      \-----12(R)
|             \-----11(B)
|                    \-----10(R)
9(B)
|                    /-----8(R)
|             /-----7(B)
|      /-----6(R)
|      |      \-----5(B)
\-----4(B)
       |             /-----3(R)
       |      /-----2(B)
       \-----1(R)
              \-----0(B)


------------------------------------------------------------

对于这组测试数据的每一步的插入的图的步骤在博客:(7条消息) 红黑树从头至尾插入和删除结点的全程演示图_结构之法 算法之道-CSDN博客_红黑树的插入和删除

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

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

相关文章

Tokitsukaze and a+b=n (hard)(差分)

C-Tokitsukaze and abn (hard)_2023牛客寒假算法基础集训营2 (nowcoder.com) 题目描述 Tokitsukaze有一个整数n&#xff0c;以及m个区间[L, R]。 她想知道有多少种选法&#xff0c;满足:从m个区间中选择两个区间[L; R],[Lj;,R](i≠j)&#xff0c;并从第一个区间选择一个整数a(…

redis为什么需要主从复制?

为什么要有主从复制&#xff0c;使redis具有高可用性&#xff01; 多机情况下主从复制 同步文件和同步命令 同步文件 客户端发送命令 slaveof 给从服务器从服务器发送 sync 命令给主服务器&#xff0c;主服务器收到以后&#xff0c;会执行bgsave命令 生成rdb文件&#xff0c;同…

2.3总线仲裁

文章目录一、引子二、总线仲裁三、集中仲裁方式1.链式查询方式&#xff08;1&#xff09;介绍&#xff08;2&#xff09;过程&#xff08;3&#xff09;特点&#xff08;4&#xff09;优缺点①优点②缺点2.计数器查询方式&#xff08;1&#xff09;介绍&#xff08;2&#xff0…

在线支付系列【4】支付安全之数字签名

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 文章目录信息摘要摘要算法数据完整性数字签名签名流程验签流程实现代码信息摘要 信息摘要就是一段数据的特征信息&#xff0c;当数据发生了改变&#xff0c;信息摘要也会发生改变&#xff0c;发送方会将…

12月知识小报|线上问题的抽丝剥茧与一锤定音

海恩法则是德国飞机涡轮机的发明者帕布斯海恩提出的一个在航空界关于飞行安全的法则。每一起严重事故的背后&#xff0c;必然有29次轻微事故和300起未遂先兆以及1000起事故隐患。作为开发者&#xff0c;安全生产是我们底线&#xff0c;敬畏每一行代码&#xff0c;挖掘每一个故障…

LINUX学习之文件基本属性(二)

查看文件属性 Linux 系统是一种典型的多用户系统&#xff0c;不同的用户处于不同的地位并拥有不同的权限。在 Linux 系统中&#xff0c;通常使用 chown 命令来修改文件或目录的所有者&#xff0c;chmod 命令则用于设置用户的权限。 chown (change owner) &#xff1a;用于修改…

Linux下的进程通信之system V共享内存

目录 使用system V共享内存进行进程间通信&#xff1a; 获取共享内存shmget 将共享内存关联到进程 去关联共享内存 删除共享内存 简易模拟实现server和client之间的通信&#xff1a; 服务端代码&#xff1a; 客户端代码&#xff1a; 共享内存的特点&#xff1a; 其他…

solrCloud一:zookeeper集群搭建

SolrCloud是基于Solr和Zookeeper的分布式搜索方案&#xff0c;它的主要思想是使用Zookeeper作为集群的配置信息中心。SolrCloud是Solr的一种分布式部署方式 &#xff0c;当索引越来越大时&#xff0c;一个单一的系统无法满足空间和查询效率上的要求&#xff0c;这个时候往往需要…

SpringCloud(11):Hystrix请求合并

1 简介 如图&#xff0c;多个客户端发送请求调用(消费者)项目中的findOne方法&#xff0c;这时候在这个项目中的线程池中会发申请与请求数量相同的线程数&#xff0c;对EurekaServiceProvider(服务提供者)的getUserById方法发起调用&#xff0c;每个线程都要调用一次&#xff0…

圆满落幕!56 人参加,龙蜥社区技术委员会、运营委员会会议顺利完成

1 月 13 日&#xff0c;龙蜥社区分别召开了第 10 次技术委员会会议和第 14 次运营委员会会议&#xff0c;来自 21 家理事单位的委员代表出席。两个会上分别总结和回顾了龙蜥社区 2022 年度整体技术和运营发展情况&#xff0c;就社区产品、重要技术决策、社区治理、2023 年度运营…

塔望3W消费战略全案丨火出天际的预制菜,能否拯救开饭焦虑?

2022年6月塔望咨询开设塔望食品大健康消费研究院&#xff08;简称塔望食研院&#xff09;栏目&#xff0c;塔望食研院以“为食品行业品牌高质量发展赋能”为理念&#xff0c;将不定期发布食品大健康行业研究、消费研究报告。塔望食研院致力于结合外部数据、消费调研数据、企业内…

Web(九)

Web服务器软件Tomcat web服务器软件&#xff1a; 服务器&#xff1a;安装了服务器软件的计算机 服务器软件&#xff1a;接收用户的请求&#xff0c;处理请求&#xff0c;做出响应 web服务器软件&#xff1a;接收用户的请求&#xff0c;处理请求&#xff0c;做出响应。 在…

说话人识别损失函数的PyTorch实现与代码解读

概述 说话人识别中的损失函数分为基于多类别分类的损失函数&#xff0c;和端到端的损失函数&#xff08;也叫基于度量学习的损失函数&#xff09;&#xff0c;关于这些损失函数的理论部分&#xff0c;可参考说话人识别中的损失函数本文主要关注这些损失函数的实现&#xff0c;…

SQL 分组条件深入剖析

问题在 stackoverflow 网站上看到这样一个 SQL 分组条件的需求&#xff0c;需求看似挺简单&#xff0c;但能把 SQL 写正确对于新手来说也不容易&#xff0c;我们拿过来深入剖析一下&#xff0c;数据如下&#xff1a;需求是查找只有Ready 状态的设备。解答自然思路&#xff1a;按…

寅辞旧岁,卯定常虹丨ASKO洗碗机“净”护新春团圆时刻

农历新年是一年中最重要的节日&#xff0c;但过去三年的特殊时光阻碍了很多人的归乡之行&#xff0c;如今当阴霾逐渐散去&#xff0c;必然会引来大规模的新年归乡潮&#xff0c;奔赴一个久违的团圆年。美馔佳宴是新春佳节的永恒命题&#xff0c;新年家里少不了亲友的光临&#…

Windows 7的最后一个版本

前天推送的文章介绍了&#xff0c;在1月10日微软发布了最后一个补丁后&#xff0c;微软为Windows7提供的所有更新(包括收费的ESU)已经完全终止。以后再有新的补丁&#xff0c;则为第三方平台提供的非官方版的了。 早在2022年9月份&#xff0c;微软就发布了支持安全启动UEFI的补…

DocPrompt代码实现与模型微调

数据预处理阶段 PaddleOCR PP-Structure&#xff1a;这个库其实是用于版面分析的一个开源库&#xff0c;参见&#xff1a;github: Layout-Parser/layout-parserhttps://github.com/Layout-Parser/layout-parser 代码推理阶段 Paddle-Inferencehttps://paddle-inference.readt…

图形编辑器:工具管理和切换

大家好&#xff0c;我是前端西瓜哥。今天我们看看对于一款图形编辑器&#xff0c;应该怎么去实现工具&#xff0c;比如绘制矩形、选中工具&#xff0c;以及如何去管理它们的。 项目地址&#xff0c;欢迎 star&#xff1a; https://github.com/F-star/suika 线上体验&#xff1a…

【改进篇】Python实现VRP常见求解算法——蚁群算法(ACO)

基于python语言&#xff0c;实现经典蚁群算法&#xff08;ACO&#xff09;对车辆路径规划问题&#xff08;CVRP&#xff09;进行求解&#xff0c; 优化代码结构&#xff0c;改进Split函数 目录往期优质资源1. 适用场景2. 改进效果对比2.1实验结果2.2 改进前后算法性能对比3. 求…

臻图信息构建数字孪生港口船舶停靠管理系统,赋能港口创新发展

我国的港口不仅是船只停靠的避风港&#xff0c;也是现代渔业发展和管理的中心。随着国内港口业的不断发展&#xff0c;国务院在《现代综合运输体系发展“十四五”规划》中提出&#xff0c;要自动化、数字化、智能化等技术来完善监管体系建设。 ​ 随着科技兴港战略的提出&…