洛谷 P3369:【模板】普通平衡树 ← “红黑树”实现

【题目来源】
https://www.luogu.com.cn/problem/P3369

【题目描述】
您需要动态地维护一个可重集合 M,并且提供以下操作:
(1)向 M 中插入一个数 x。
(2)从 M 中删除一个数 x(若有多个相同的数,应只删除一个)。
(3)查询 M 中有多少个数比 x 小,并且将得到的答案加一。
(4)查询如果将 M 从小到大排列后,排名位于第 x 位的数。
(5)查询 M 中 x 的前驱(前驱定义为小于 x,且最大的数)。
(6)查询 M 中 x 的后继(后继定义为大于 x,且最小的数)。
对于操作 3,5,6,不保证当前可重集中存在数 x。

【输入格式】
第一行为 n,表示操作的个数。
下面 n 行,每行有两个数 opt 和 x,opt 表示操作的序号(1≤opt≤6)。

【输出格式】
对于操作 3,4,5,6,每行输出一个数,表示对应答案。

【输入样例】
10
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598

【输出样例】
106465
84185
492737

【数据范围】
对于 100% 的数据,1≤n≤
10^5,∣x∣≤10^7

【算法分析】
红黑树(Red Black Tree)简称 RB 树,是一种自平衡的二叉排序树。红黑树在平衡二叉树的基础上牺牲严格的平衡,降低对旋转的要求,通过少量的旋转操作达到平衡,从而提高性能。
红黑树于 1972 年由鲁道夫 • 拜耳(Rudolf Bayer)发明。当时被称为平衡二叉 B 树(
红黑树本质上是一种 B 树)。B 树(又称 B- 树)中的 B 表示 Balanced,即平衡的意思。
红黑树自提出以来,经历了多次改进和优化,应用非常广泛。

下面关于红黑树的描述,来源于李冬梅、严蔚敏、吴伟民等编著的《数据结构(C语言版 第3版)》。其 ISBN 为 9787115651259。

 ● 红黑树的性质
(1)每个结点的颜色不是黑色就是红色。
(2)根结点一定是黑色。
(3)每个
叶子结点都是黑色的空结点,即虚构的外部结点、NULL结点。
(4)
两个红色结点不能相邻,即红色结点的孩子结点一定是黑色。但是,两个黑色结点可以相邻
(5)对任一结点,从该结点出发到叶子结点的任一路径所包含的黑色结点数目相同,即
黑平衡

● 红黑树的插入
在对红黑树进行插入操作时,首先需要考虑的一个问题是新插入红黑树的结点颜色是红色还是黑色?
一般情况下,设置新插入结点的颜色为红色。主要原因是当新插入结点的颜色为黑色时,该结点所在的路径比其他路径多出一个黑色结点,破坏上文所述的性质 5,使得红黑树的调整变得比较麻烦;当新插入结点的颜色为红色时,所有路径上的黑色结点数目不会发生改变。只有出现连续两个红色结点时会破坏上文所述的性质 4,需要通过变色或者旋转进行调整,操作相对简单。
设结点 x 为新插入结点,插入过程描述如下:
(一)如果红黑树初始为空树,那么新结点 x 插入后是树的根结点,将结点 x 着色为黑色即可;
(二)新结点 x 的父结点为黑色,则不需要进行任何调整;
(三)新结点 x 的父结点为红色,叔结点也为红色,那么将其祖父结点,父结点,叔结点的颜色翻转,并检查其祖父结点是否为根结点;
(四)新结点 x 的父结点为红色,叔结点为黑色,该种情况较为复杂。
1.
LL型(父结点为祖父结点的左孩子,新结点 x 为父结点的左孩子):右旋祖父节点,之后将父结点与祖父结点的颜色翻转;
2.
RR型(父结点为祖父结点的右孩子,新结点 x 为父结点的右孩子):左旋祖父节点,之后将父结点与祖父结点的颜色翻转;
3. LR型(父结点为祖父结点的左孩子,新结点 x 为父结点的右孩子):左旋新结点,右旋祖父节点,之后将新结点与祖父结点的颜色翻转;
4.
RL型(父结点为祖父结点的右孩子,新结点 x 为父结点的左孩子):右旋新结点,左旋祖父节点,之后将新结点与祖父结点的颜色翻转。

● 红黑树的删除
红黑树的删除操作比较复杂。下面
关于删除操作的描述忽略外部结点 NULL。删除过程描述如下:
(一)
待删结点有两个孩子结点。首先找到待删结点的中序后继结点,然后与待删结点进行交换,此时待删结点就变成了叶子结点或只有一个右孩子结点。即问题就转换为了删除叶子结点或待删结点只有一个孩子结点的情况;
(二)
待删结点只有一个孩子结点。那么待删结点必为黑色,子结点必为红色,此时只需将子结点顶上去,再着成黑色即可;
(三)
待删结点没有子结点,即删除叶子结点。
1.待删叶子结点为红色:直接删除,不需要进行任何调整。
2.待删叶子结点为黑色,且其兄弟结点有红色孩子结点。
(1)LL型(待删叶子结点的兄弟是其父结点的左孩子,兄弟结点的左孩子为红色):交换兄弟结点及父结点的颜色,且将兄弟结点的左孩子着为黑色后,右旋父结点,删除叶子结点。
(2)RR型(待删叶子结点的兄弟是其父结点的右孩子,兄弟结点的右孩子为红色):交换兄弟结点及父结点的颜色,且将兄弟结点的右孩子着为黑色后,左旋父结点,删除叶子结点。
(3)LR型(待删叶子结点的兄弟是其父结点的左孩子,兄弟结点的右孩子为红色):首先将兄弟结点变成红色,兄弟结点的右孩子变成黑色,然后左旋兄弟结点的右孩子。。
(4)RL型(待删叶子结点的兄弟是其父结点的右孩子,兄弟结点的左孩子为红色):首先将兄弟结点变成红色,兄弟结点的左孩子变成黑色,然后右旋兄弟结点的左孩子。
3.待删叶子结点为黑色,且其兄弟结点没有红色孩子结点。
(1)待删叶子结点的父结点为红色:将父结点变为黑色,将兄弟结点变为红色,删除叶子结点。
(2)待删叶子结点的父结点为黑色:将父结点变为红色,将兄弟结点变为黑色,左旋父结点,删除叶子结点。

● 从某个结点出发(不含该结点)到达一个叶子结点的任意一条简单路径上的黑色结点个数称为该结点的
黑高。根结点的黑高,称为红黑树的黑高度。

● this->root->left‌ 的涵义
‌this->root->left‌ 表示访问当前对象的根节点(root)的左子节点(left)。在 C++ 中,这种表达方式用于访问类的成员变量。
具体来说,this 关键字用于指向当前对象的实例,而 -> 操作符用于通过指针访问对象的成员。因此,this->root->left 表示访问当前对象的根节点的左子节点。

【算法代码】

#include <bits/stdc++.h>
using namespace std;

const int black=1;
const int red=0;

struct Node { //Red Black Tree's node
    Node *left,*right;
    int key,vol;
    int color;
    Node(int x):key(x),vol(1),color(red) {};
};

class RBTree {
    private:
        int vol;
        Node *root,*next;
        bool ans;

    public:
        RBTree() {
            root = new Node(0);
            root->color = black;
            root->left = root->right = root;
            root->vol = 0;
            next = nullptr;
            vol = 0;
        }

        Node *search(Node *&cur, int &key) {
            if(cur == root) return nullptr;
            else if(cur->key>key) return search(cur->left,key);
            else if(cur->key<key) return search(cur->right,key);
            else return cur;
        }

        bool isBalance() {
            Node *root = this->root->left;
            if(root->color == red) return false;
            int cnt = 0; //number of black nodes in the leftmost path
            auto left = root;
            while(left != this->root) {
                if(left->color == black) cnt++;
                left = left->left;
            }
            int blackNum = 0;
            return _isBalance(root, this->root,cnt,blackNum);
        }

        bool insert(int x) {
            ans = false;
            insert(root->left, x);
            root->left->color = black;
            return ans;
        }

        bool erase(int x) {
            ans = false;
            next = nullptr;
            erase(root->left,x);
            root->left->color = black;
            return ans;
        }

        int get_rank(int x) {
            Node *cur = root->left;
            int rank = 1;
            while(cur != root && cur != nullptr) {
                if(x <= cur->key) cur = cur->left;
                else {
                    rank += cur->left->vol+1;
                    cur = cur->right;
                }
            }
            return rank;
        }

        int get_val(int rank) {
            Node *cur = root->left;
            while(cur != root && cur != nullptr) {
                if(cur->left->vol+1 == rank) break;
                else if(cur->left->vol >= rank) cur = cur->left;
                else {
                    rank -= cur->left->vol+1;
                    cur = cur->right;
                }
            }
            return cur->key;
        }

        int get_pre(int x) {
            Node *p = root->left;
            int pre;
            while(p != root && p != nullptr) {
                if(p->key<x) {
                    pre = p->key;
                    p = p->right;
                } else p = p->left;
            }
            return pre;
        }

        int get_next(int x) {
            Node *p = root->left;
            int next;
            while(p != root && p != nullptr) {
                if(p->key>x) {
                    next = p->key;
                    p = p->left;
                } else p = p->right;
            }
            return next;
        }

    private:
        bool _isBalance(Node *root, Node *parent, int base, int blackNum) {
            if(root == this->root) {
                if(base != blackNum) return false;
                return true;
            }
            if(root->color == red && parent->color == red) return false;
            if(root->color == black) blackNum++;
            return _isBalance(root->left, root, base, blackNum) && _isBalance(root->right, root, base, blackNum);
        }

        void left_rotate(Node *&T) {
            Node *right = T->right;
            T->right = right->left;
            right->left = T;
            T = right;
            update(T->left), update(T);
        }

        void right_rotate(Node *&T) {
            Node *left = T->left;
            T->left = left->right;
            left->right = T;
            T = left;
            update(T->right), update(T);
        }

        bool hasRedChild(Node *&T) {
            if(T == root) return false;
            return T->left->color == red || T->right->color == red;
        }

        void update(Node *cur) {
            if(cur == root) return;
            cur->vol = cur->left->vol+cur->right->vol+1;
        }

        bool precursor(Node *&cur, int &key) {
            bool needSave = false;
            if(cur->right == root) {
                Node *temp = cur;
                key = cur->key;
                cur = cur->left;
                if(temp->color == red) needSave = false;
                else if(cur->color == red) {
                    cur->color = black;
                    needSave = false;
                } else {
                    next = cur;
                    needSave = true;
                }
                delete temp;
                return needSave;
            }
            needSave = precursor(cur->right, key);

            if(needSave) return _erase(cur);
            update(cur);
            return needSave;
        }

        bool erase(Node *&cur, int &key) {
            bool needSave = false;
            if(cur == root) return ans=false;
            else if(cur->key < key)
                needSave = erase(cur->right, key);
            else if(cur->key > key) needSave = erase(cur->left, key);
            else {
                ans = true;
                if(cur->left == root) {
                    Node *temp = cur;
                    cur = cur->right;
                    if(temp->color == red) needSave = false;
                    else if(cur->color == red) {
                        cur->color = black;
                        needSave = false;
                    } else {
                        next = cur;
                        needSave = true;
                    }

                    delete temp;
                    return needSave;
                } else if(cur->right == root) {
                    Node *temp = cur;
                    cur = cur->left;
                    if(temp->color == red) needSave = false;
                    else if(cur->color == red) {
                        cur->color = black;
                        needSave = false;
                    } else {
                        next = cur;
                        needSave = true;
                    }

                    delete temp;
                    return needSave;
                }

                needSave = precursor(cur->left, cur->key);
            }

            if(needSave) return _erase(cur);
            update(cur);
            return needSave;
        }

        bool _erase(Node *&cur) {
            update(cur);
            if(next == nullptr) return false;
            else if(cur->left == next) {
                next = nullptr;
                if(cur->right->color == red) {
                    cur->right->color = black;
                    cur->color = red;
                    left_rotate(cur);
                    next = cur->left->left;
                    return _erase(cur->left);
                }

                if(hasRedChild(cur->right)) {
                    bool color = cur->color;
                    if(cur->right->right->color == black) right_rotate(cur->right);
                    left_rotate(cur);
                    cur->color = color;
                    cur->left->color = cur->right->color = black;
                    return false;
                } else if (cur->color == red) {
                    cur->color = black;
                    cur->right->color = red;
                    return false;
                } else if (cur->color == black) {
                    cur->right->color = red;
                    next = cur;
                    return true;
                }
            } else if(cur->right == next) {
                next = nullptr;
                if(cur->left->color == red) {
                    cur->left->color = black;
                    cur->color = red;
                    right_rotate(cur);
                    next = cur->right->right;
                    return _erase(cur->right);
                }

                if(hasRedChild(cur->left)) {
                    bool color = cur->color;
                    if(cur->left->left->color == black) left_rotate(cur->left);
                    right_rotate(cur);
                    cur->color = color;
                    cur->left->color = cur->right->color = black;
                    return false;
                } else if(cur->color == red) {
                    cur->color = black;
                    cur->left->color = red;
                    return false;
                } else if(cur->color == black) {
                    next = cur;
                    cur->left->color = red;
                    return true;
                }
            }
            return false;
        }

        bool insert(Node *&cur, int &key) {
            bool needSave = false;
            if(cur == root) {
                cur = new Node(key);
                cur->left = cur->right = root;
                vol++;
                ans = true;
                return true;
            } else if(key > cur->key) needSave = insert(cur->right, key);
            else needSave = insert(cur->left, key);

            if(needSave) return _insert(cur);
            update(cur);
            return needSave;
        }

        bool _insert(Node *&cur) { //double red
            update(cur);
            if(!hasRedChild(cur)) return false;
            else if(cur->left->color == red && cur->right->color == red) {
                if(!hasRedChild(cur->left) && !hasRedChild(cur->right)) return false;
                cur->left->color = cur->right->color = black;
                cur->color = red;
                return true;
            } else if(cur->left->color == red) {
                if(!hasRedChild(cur->left)) return true;
                if(cur->left->left->color == black) left_rotate(cur->left);
                right_rotate(cur);
                cur->color = black;
                cur->right->color = red;
                return false;
            } else if(cur->right->color == red) {
                if(!hasRedChild(cur->right)) return true;
                if(cur->right->right->color == black) right_rotate(cur->right);
                left_rotate(cur);
                cur->color = black;
                cur->left->color = red;
                return false;
            }
            return true;
        }
};

int main() {
    RBTree it;
    int n;
    cin>>n;
    while(n--) {
        int op,x;
        cin>>op>>x;
        if(op==1) it.insert(x);
        else if(op==2) it.erase(x);
        else if(op==3) cout<<it.get_rank(x)<<endl;
        else if(op==4) cout<<it.get_val(x)<<endl;
        else if(op==5) cout<<it.get_pre(x)<<endl;
        else if(op==6) cout<<it.get_next(x)<<endl;
    }
    cout<<endl;
    return 0;
}

/*
in:
10
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598

out:
106465
84185
492737
*/




【参考文献】
https://blog.csdn.net/Worthy_Wang/article/details/113853780
https://www.cnblogs.com/fush/p/18639446/RBT-AAT
https://www.cnblogs.com/jcwy/p/18513114
https://www.geeksforgeeks.org/c-program-red-black-tree-insertion/
https://www.sanfoundry.com/cpp-program-implement-red-black-tree/
https://codeofcode.org/lessons/red-black-trees-in-cpp/
https://www.geeksforgeeks.org/red-black-tree-in-cpp/
https://www.cs.usfca.edu/~galles/visualization/Algorithms.html
https://blog.csdn.net/qq_16762209/article/details/134561421
https://blog.csdn.net/qq_16762209/article/details/134431094




 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

微信小程序实现登录注册

文章目录 1. 官方文档教程2. 注册实现3. 登录实现4. 关于作者其它项目视频教程介绍 1. 官方文档教程 https://developers.weixin.qq.com/miniprogram/dev/framework/路由跳转的几种方式&#xff1a; https://developers.weixin.qq.com/miniprogram/dev/api/route/wx.switchTab…

嵌入式系统 (2.嵌入式硬件系统基础)

2.嵌入式硬件系统基础 2.1嵌入式硬件系统的组成 嵌入式硬件系统以嵌入式微处理器为核心&#xff0c;主要由嵌入式微处理器、总线、存储器、输入/输出接口和设备组成。 嵌入式微处理器 嵌入式微处理器采用冯诺依曼结构或哈佛结构&#xff1a;前者指令和数据共享同一存储空间…

多模态大模型初探索:通过ollama部署多模态大模型

文章目录 前言模型下载 前言 今天和同事聊天&#xff0c;聊到多模态大模型&#xff0c;感觉可以作为2025年的一个新的探索方向。希望和大家一起学习&#xff0c;一起进步。 今天也是尝试了我能想到的最基本最快速地本地部署多模态大模型的方式&#xff0c;那便是使用ollama。…

【超详细】React SSR 服务端渲染实战

前言 这篇文章和大家一起来聊一聊 React SSR&#xff0c;本文更偏向于实战。你可以从中学到&#xff1a; 从 0 到 1 搭建 React SSR 服务端渲染需要注意什么 react 18 的流式渲染如何使用 文章如有误&#xff0c;欢迎指出&#xff0c;大家一起学习交流&#xff5e;。 &…

js策略模式

定义一组算法&#xff0c;将每个算法封装成一个独立的类&#xff0c;并使它们可以互相替换。策略模式使得算法的变化不会影响到使用算法的客户。 const priceProcessor {pre(originPrice) {if (originPrice > 100) {return originPrice - 20;}return originPrice * 0.9;}…

Python中的可变对象与不可变对象;Python中的六大标准数据类型哪些属于可变对象,哪些属于不可变对象

Python中的可变对象与不可变对象&#xff1b;Python中的六大标准数据类型哪些属于可变对象&#xff0c;哪些属于不可变对象 Python中的可变对象与不可变对象一、Python的六大标准数据类型1. 数字类型 (Number)2. 字符串 (String)3. 列表 (List)4. 元组 (Tuple)5. 集合 (Set)6. …

js状态模式

允许一个对象在其内部状态改变时改变它的行为。 状态模式将对象的状态封装成独立的类&#xff0c;并使它们可以互相转换 // 定义状态接口class State {constructor() {if (this.constructor State) {throw new Error(不能实例化抽象类);}}// 定义状态方法handle(context) {th…

基于64QAM的载波同步和定时同步性能仿真,包括Costas环和gardner环

目录 1.算法仿真效果 2.算法涉及理论知识概要 3.MATLAB核心程序 4.完整算法代码文件获得 1.算法仿真效果 matlab2022a仿真结果如下&#xff08;完整代码运行后无水印&#xff09;&#xff1a; 仿真操作步骤可参考程序配套的操作视频。 2.算法涉及理论知识概要 载波同步是…

【Web安全】SQL 注入攻击技巧详解:布尔盲注(Boolean-Based Blind SQL Injection)

【Web安全】SQL 注入攻击技巧详解&#xff1a;布尔盲注&#xff08;Boolean-Based Blind SQL Injection&#xff09; 引言 布尔盲注&#xff08;Boolean-Based Blind SQL Injection&#xff09;是一种在无法直接从数据库获取数据的情况下&#xff0c;通过观察数据库响应的布尔…

太速科技-418-基于AD9361 +ZYNQ7020 的软件无线电 SDR 套件

基于AD9361 ZYNQ7020 的软件无线电 SDR 套件 一、板卡信息 ● ZYNQ芯片采用XC7Z020&#xff0c;逻辑容量更大&#xff0c;支持更大的逻辑设计&#xff1b; ● 内存采用两片512M DDR3&#xff0c;共1GByte&#xff0c;更大容量。 ● 支持千兆网口&#xff0c;支持ZEDFMCO…

SpringBoot日常:集成Kafka

文章目录 1、pom.xml文件2、application.yml3、生产者配置类4、消费者配置类5、消息订阅6、生产者发送消息7、测试发送消息 本章内容主要介绍如何在springboot项目对kafka进行整合&#xff0c;最终能达到的效果就是能够在项目中通过配置相关的kafka配置&#xff0c;就能进行消息…

分布式IO模块:激光切割机产线高效控制的创新引擎

在智能制造的浪潮中&#xff0c;激光切割技术以其高精度、高效率的特点&#xff0c;成为了现代工业生产中不可或缺的一部分。特别是在汽车制造、航空航天、电子设备及精密零部件加工等领域&#xff0c;激光切割机以其无与伦比的切割精度和灵活性&#xff0c;引领着制造业的转型…

RK3562编译Android13 ROOT固件教程,触觉智能开发板演示

本文介绍编译Android13 ROOT权限固件的方法&#xff0c;触觉智能RK3562开发板演示&#xff0c;搭载4核A53处理器&#xff0c;主频高达2.0GHz&#xff1b;内置独立1Tops算力NPU&#xff0c;可应用于物联网网关、平板电脑、智能家居、教育电子、工业显示与控制等行业。 关闭seli…

wireshark抓包工具新手使用教程

wireshark抓包工具新手入门使用教程 一、Wireshark软件安装二、Wireshark 抓包示范三、Wireshakr抓包界面四、Wireshark过滤器设置五、wireshark过滤器表达式的规则六、Wireshark抓包分析TCP三次握手七、Wireshark分析常用列标签格式 Wireshark是一款开源的网络协议分析工具&am…

如何用Python编程实现自动整理XML发票文件

传统手工整理发票耗时费力且易出错&#xff0c;而 XML 格式发票因其结构化、标准化的特点&#xff0c;为实现发票的自动化整理与保存提供了可能。本文将详细探讨用python来编程实现对 XML 格式的发票进行自动整理。 一、XML 格式发票的特点 结构化数据&#xff1a;XML 格式发票…

【网络安全 | 漏洞挖掘】HubSpot 全账户接管(万字详析)

未经许可,不得转载。 今天我们将分享一个关于在 Bugcrowd 平台的 HubSpot 公共漏洞赏金计划中实现全账户接管的故事。 文章目录 正文SQL 注入主机头污染(Host Header Poisoning)负载均衡器主机头覆盖(Load Balancer Host Header Override)Referer Header 测试ORIGIN Heade…

2025_0105_生活记录

3号去内蒙看了流星雨。还记得上次看流星的时间是2018年&#xff0c;也是冬天&#xff0c;大家在雁栖湖校区的操场上仰望星空。那个时候幸运的看到了一颗流星&#xff0c;便迅速地在心里许愿。这次看到了三颗流星&#xff0c;我也许了愿&#xff0c;希望实现。 24年走过了十多个…

(四)ROS通信编程——服务通信

前言 学完了话题通信其实操作流程基本都已经很熟悉了&#xff0c;因此服务通讯的学习就会流畅许多。 服务通信也是ROS中一种极其常用的通信模式&#xff0c;服务通信是基于请求响应模式的&#xff0c;是一种应答机制。也即: 一个节点A向另一个节点B发送请求&#xff0c;B接收…

《繁星路》V1.8.3(Build16632266)官方中文学习版

《繁星路》官方中文版https://pan.xunlei.com/s/VODae2_2Z3QyMF02I5y321uHA1?pwdqgsh# 作为一款星际模拟游戏&#xff0c;完美融合了硬科幻元素与基地建设玩法&#xff0c;体验改造行星的恢弘与壮阔。化身人工意识AMI&#xff0c;遵照基本指示推进火星改造的各项工作&#xf…

学习threejs,导入wrl格式的模型

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️THREE.VRMLLoader wrl模型加…