当前位置: 首页 > news >正文

深入剖析红黑树:优雅地平衡二叉搜索树

在这里插入图片描述

目录

  • 一.红黑树的概念
    • 二.插入操作
      • 三.与AVL树的比较

一.红黑树的概念

在之前的学习中,我们了解了二叉搜索平衡树,AVL树通过控制每个结点中的平衡因子的绝对值不超过1,实现了一个高性能的树。而相较于AVL的高度平衡,红黑树觉得AVL为了平衡也付出了代价(插入和删除时进行了多次旋转),所以红黑树在控制平衡上面没有这么严格,只是要求,最长路径不超过最短路径的二倍。那红黑树又是如何控制实现的呢?接下来了解一下红黑树的性质:

  1. 每个结点不是红色就是黑色
  2. 根节点是黑色的
  3. 如果一个节点是红色的,则它的两个孩子结点是黑色的(任何路径上没有连续两个红结点)
  4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点
  5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点,也称为NIL结点

二.插入操作

在我们了解红黑树的性质后,就需要分析相关代码看他如何实现的。首先我们看红黑树结点的定义:
因为map和set的底层使用红黑树实现了,为了之后方便,这里红黑树用了两个模板参数。

#include <iostream>using namespace std;enum Colour
{RED, BLACK
};template<class K,class V>
class RBTreeNode
{
public:RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;pair<K, V> _kv;Colour _col;RBTreeNode(const pair<K,V>& kv):_left(nullptr),_right(nullptr),_parent(nullptr),_kv(kv),_col(RED){}
};

结点定义中与AVL树差距不大,只是多了个用枚举定义的参数,用来指定是红结点还是黑结点。接下来讲解重点的插入操作:
首先因为红黑树也是二叉搜索树,所以要满足二叉搜索树的基本性质,再者是我们插入的结点的颜色先置为什么能让后面的调整更方便呢。如果黑色需要在后面依据性质4调整,插入红色的话依据性质3调整。明显是4更为复杂,所以我们插入颜色为红色

bool Insert(const pair<K, V>& kv)
{if(_root == nullptr){_root = new Node(kv);_root->_col = BLACK;return true;}Node* cur = _root;Node* parent = nullptr;while (cur){if (kv.first > cur->_kv.first){parent = cur;cur = cur->_right;}else if (kv.first < cur->_kv.first){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(kv);cur->_col = RED;if (parent->_kv.first > cur->_kv.first){parent->_left = cur;}else{parent->_right = cur;}cur->_parent = parent;///开始调整颜色///开始调整颜色_root->_col = BLACK;return true;
}

上段代码是不涉及调整颜色,只保证二叉搜索树性质。下面开始分类讨论研究如何调整颜色。
在这里插入图片描述

如上图所示,插入的cur结点是红色,这时出现了连续的两个红结点,所以就要进行调整。如果parent结点是黑色则不需要调整。我们把10结点和20结点称为parent和grandfather结点,22为uncle结点
1.当uncle结点为红色时,变色然后继续向上调整
在这里插入图片描述
2.当uncle结点不存在时或者为黑时的处理方式相同:
在这里插入图片描述
完整代码如下:

	bool Insert(const pair<K, V>& kv){if(_root == nullptr){_root = new Node(kv);_root->_col = BLACK;return true;}Node* cur = _root;Node* parent = nullptr;while (cur){if (kv.first > cur->_kv.first){parent = cur;cur = cur->_right;}else if (kv.first < cur->_kv.first){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(kv);cur->_col = RED;if (parent->_kv.first > cur->_kv.first){parent->_left = cur;}else{parent->_right = cur;}cur->_parent = parent;while (parent && parent->_col == RED){Node* grandfather = parent->_parent;if (parent == grandfather->_left){Node* uncle = grandfather->_right;//叔叔存在且为红if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else // 叔叔不存在或者为黑都是旋转+变色{if (cur == parent->_left){RevoR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{RevoL(parent);RevoR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}}}else{Node* uncle = grandfather->_left;//叔叔存在且为红if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else // 叔叔不存在或者为黑都是旋转+变色{if (cur == parent->_left){RevoR(parent);RevoL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}else{RevoL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}}}}_root->_col = BLACK;return true;}void RevoL(Node* parent){Node* cur = parent->_right;Node* curleft = cur->_left;parent->_right = curleft;//无论curleft是否为空都要执行这一步if (curleft){curleft->_parent = parent;}cur->_left = parent;Node* ppnode = parent->_parent;parent->_parent = cur;if (parent == _root){_root = cur;cur->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = cur;}else{ppnode->_right = cur;}cur->_parent = ppnode;}}void RevoR(Node* parent){Node* cur = parent->_left;Node* curright = cur->_right;parent->_left = curright;if (curright){curright->_parent = parent;}cur->_right = parent;Node* ppnode = parent->_parent;parent->_parent = cur;if (_root == parent)//等价于 ppnode == nullptr{_root = cur;cur->_parent = nullptr;}else{cur->_parent = ppnode;if (ppnode->_left == parent){ppnode->_left = cur;}else{ppnode->_right = cur;}}}

三.与AVL树的比较

红黑树和AVL树的插入效率O(logN),只是红黑树不像AVL追求如此平衡,所以旋转次数会少,并且实现也较简单。所以在实践中大都使用红黑树。之后我们还是使用红黑树模拟实现map和set

相关文章:

深入剖析红黑树:优雅地平衡二叉搜索树

目录 一.红黑树的概念二.插入操作三.与AVL树的比较 一.红黑树的概念 在之前的学习中&#xff0c;我们了解了二叉搜索平衡树&#xff0c;AVL树通过控制每个结点中的平衡因子的绝对值不超过1&#xff0c;实现了一个高性能的树。而相较于AVL的高度平衡&#xff0c;红黑树觉得AVL为…...

C10K问题:高并发模型设计

一、循环服务器模型 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <signal.h> #include <sys/types.h> #include <sys/socket.h> //*******// #include &l…...

哈希/散列--哈希表[思想到结构][==修订版==]

文章目录 1.何为哈希?1.1百度搜索1.2自身理解1.3哈希方法/散列方法1.4哈希冲突/哈希碰撞1.5如何解决?哈希函数的设计 2.闭散列和开散列2.1闭散列/开放定址法2.2开散列/链地址法/开链法1.概念2.容量问题3.字符串问题4.开散列性能测试5.开散列与闭散列比较 3.代码实现[配备详细…...

成都建筑模板批发市场在哪?

成都作为中国西南地区的重要城市&#xff0c;建筑业蓬勃发展&#xff0c;建筑模板作为建筑施工的重要材料之一&#xff0c;在成都也有着广泛的需求。如果您正在寻找成都的建筑模板批发市场&#xff0c;广西贵港市能强优品木业有限公司是一家值得关注的供应商。广西贵港市能强优…...

亨元模式 结构型模式之六

1.定义 享元模式是一种结构型设计模式&#xff0c; 它允许你在消耗少量内存的情况下支持大量对象。 2.滑滑梯问题 在说明亨元模式之前&#xff0c;我们先看看关于滑滑梯的程序设计。小区的楼下只有三个滑滑梯&#xff0c;但是想玩的小朋友却非常多。怎么设计计滑滑梯资源的管理…...

面试题: Spring中Bean的实例化和Bean的初始化有什么区别?

Spring中Bean的实例化和Bean的初始化有什么区别? 背景答案扩展知识什么是实例化什么是初始化 个人评价我的回答 背景 想换工作, 看了图灵周瑜老师的视频想记录一下, 算是学习结果的一个输出. 答案 Spring 在创建一个Bean对象时, 会先创建出一个Java对象, 会通过反射来执行…...

阻塞队列,生产者消费者模型

目标&#xff1a; 1. 认识与使用阻塞队列 2. 认识与实现消费者模型 目录 阻塞队列的特点 生产者消费者模型 生产者消费者模型的优点 阻塞队列实现该模型 阻塞队列的特点 1. 线程安全 2. 带有阻塞特性 &#xff08;1&#xff09;如果队列为空&#xff0c;继续出队列&a…...

【RCRL充放电时间相关计算】

一. 基础知识 L、C元件称为“惯性元件”&#xff0c;即电感中的电流、电容器两端的电压&#xff0c;都有一定的“电惯性”&#xff0c;不能突然变化。充放电时间&#xff0c;不光与L、C的容量有关&#xff0c;还与充/放电电路中的电阻R有关。RC电路的时间常数&#xff1a;τRC…...

C++ primer plus--输入、输出和文件

17 输入、输出和文件 17.1 C 输入和输出概述 C 把输入和输出看做字节流。输入时&#xff0c;程序从输入流中抽取字节&#xff1b;输出时&#xff0c;程序将字节插到输出流中。 缓冲区是内存中的临时存储区域&#xff0c;是程序与文件或其他 I/O 设备之间的桥梁。 17.2 使用…...

案例题--Web应用考点

案例题--Web应用考点 负载均衡技术微服务XML和JSON无状态和有状态真题 在选择题中没有考察过web的相关知识&#xff0c;主要就是在案例分析题中考察 负载均衡技术 应用层负载均衡技术 传输层负载均衡技术 就近的找到距离最近的服务器&#xff0c;并进行分发 使用户就近获取…...

MySQL的SQL 优化:提升数据库性能

1. 插入操作优化 1.1 使用多值插入 通常情况下&#xff0c;插入大量数据时&#xff0c;使用多值插入语句比逐行插入更高效。例如&#xff0c;将多个数据行打包成一个 INSERT 语句&#xff1a; INSERT INTO users (name, email) VALUES (Alice, aliceexample.com), (Bob, bob…...

【匠心打造】从0打造uniapp 可视化拖拽设计 c_o 第十篇

一、click one for uniapp置顶&#xff1a; 全部免费开源 (你商业用途也没关系&#xff0c;不过可以告诉我公司名或者项目名&#xff0c;放在官网上好看点。哈哈-_-) 二、写在之前 距离上一篇更新已经大约4个月了&#xff0c;公司的事情&#xff0c;自己的一些琐事一直没时间…...

BIT-5-操作符详解(C语言初阶学习)

1. 各种操作符的介绍。 2. 表达式求值 1. 操作符分类&#xff1a; 算术操作符 移位操作符 位操作符 赋值操作符 单目操作符 关系操作符 逻辑操作符 条件操作符 逗号表达式 下标引用、函数调用和结构成员 2. 算术操作符 - * / % 除了 % 操作符…...

【重拾C语言】三、分支程序设计(双分支和单分支程序设计、逻辑判断、多分支程序设计、枚举类型表示;典型例题:判断闰年和求一元二次方程根)

目录 前言 三、分支程序设计 3.1 判断成绩是否及格——双分支程序设计 3.2 成绩加上获奖信息—单分支程序设计 3.3 逻辑判断——布尔类型 3.4 获奖分等级——多分支程序设计 3.5 表示汽车种类——枚举类型 3.6 例题 3.6.1 例题——判断某个年份是否闰年 3.6.2 例题—…...

Shiro应用到Web Application

一、权限基础 a) 认证(你是谁&#xff1f;) 判断你(被认证者)是谁的过程。通常被认证者提供用户名和密码。 常见的认证包含如下几种&#xff1a; 匿名认证&#xff1a;允许访问资源&#xff0c;不做任何类型的安全检查。表单认证&#xff1a;访问资源之前&#xff0c;需要提…...

【POST请求-腾讯翻译君-爬虫案例】

原因&#xff1a;尝试多个在线翻译平台&#xff0c;由于返回数据存在加密原因&#xff08;暂时不会解密&#xff09;&#xff0c;最总找到 ”腾讯翻译君“ 完成爬虫案例POST请求测试 案例测试网址 腾讯翻译 &#xff1a;https://fanyi.qq.com/ import requests import jsoncla…...

多卡片效果悬停效果

效果展示 页面结构 从页面的结构上看&#xff0c;在默认状态下毛玻璃卡片是有层次感的效果叠加在一起&#xff0c;并且鼠标悬停在卡片区域后&#xff0c;卡片整齐排列。 CSS3 知识点 transform 属性的 rotate 值运用content 属性的 attr 值运用 实现页面整体布局 <div …...

首饰饰品经营商城小程序的作用是什么

首饰如耳钉、戒指、手镯等除了高价值产品外&#xff0c;还有很多低价产品&#xff0c;市场需求客户众多&#xff0c;在实际经营中&#xff0c;商家们也会面临一些痛点。 私域话题越来越多加之线上线下同行竞争、流量匮乏等&#xff0c;更对商家选择自建商城经营平台。 通过【…...

华为OD机试真题【服务器能耗统计】

1、题目描述 【服务器能耗统计】 服务器有三种运行状态:空载、单任务、多任务,每个时间片的能耗的分别为1、3、4; 每个任务由起始时间片和结束时间片定义运行时间; 如果一个时间片只有一个任务需要执行,则服务器处于单任务状志; 如果一个时间片有多个任务需要执行,则服务器处于…...

ubuntu按下del却出现空格(命令行下键盘错乱)

问题&#xff1a; 有一天远程我的ubuntu 20.04&#xff0c;发现为何按 del 会产生空格后移的效果&#xff0c;up键也会重叠显示&#xff0c;首先感觉是这个远程软件有问题&#xff0c;于是又换了xshell&#xff0c;发现还是不行&#xff0c;只能打开积灰已久的笔记本&#xff0…...

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...

测试微信模版消息推送

进入“开发接口管理”--“公众平台测试账号”&#xff0c;无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息&#xff1a; 关注测试号&#xff1a;扫二维码关注测试号。 发送模版消息&#xff1a; import requests da…...

在软件开发中正确使用MySQL日期时间类型的深度解析

在日常软件开发场景中&#xff0c;时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志&#xff0c;到供应链系统的物流节点时间戳&#xff0c;时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库&#xff0c;其日期时间类型的…...

Zustand 状态管理库:极简而强大的解决方案

Zustand 是一个轻量级、快速和可扩展的状态管理库&#xff0c;特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...

拉力测试cuda pytorch 把 4070显卡拉满

import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试&#xff0c;通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小&#xff0c;增大可提高计算复杂度duration: 测试持续时间&#xff08;秒&…...

【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)

1.获取 authorizationCode&#xff1a; 2.利用 authorizationCode 获取 accessToken&#xff1a;文档中心 3.获取手机&#xff1a;文档中心 4.获取昵称头像&#xff1a;文档中心 首先创建 request 若要获取手机号&#xff0c;scope必填 phone&#xff0c;permissions 必填 …...

掌握 HTTP 请求:理解 cURL GET 语法

cURL 是一个强大的命令行工具&#xff0c;用于发送 HTTP 请求和与 Web 服务器交互。在 Web 开发和测试中&#xff0c;cURL 经常用于发送 GET 请求来获取服务器资源。本文将详细介绍 cURL GET 请求的语法和使用方法。 一、cURL 基本概念 cURL 是 "Client URL" 的缩写…...

嵌入式常见 CPU 架构

架构类型架构厂商芯片厂商典型芯片特点与应用场景PICRISC (8/16 位)MicrochipMicrochipPIC16F877A、PIC18F4550简化指令集&#xff0c;单周期执行&#xff1b;低功耗、CIP 独立外设&#xff1b;用于家电、小电机控制、安防面板等嵌入式场景8051CISC (8 位)Intel&#xff08;原始…...

LLaMA-Factory 微调 Qwen2-VL 进行人脸情感识别(二)

在上一篇文章中,我们详细介绍了如何使用LLaMA-Factory框架对Qwen2-VL大模型进行微调,以实现人脸情感识别的功能。本篇文章将聚焦于微调完成后,如何调用这个模型进行人脸情感识别的具体代码实现,包括详细的步骤和注释。 模型调用步骤 环境准备:确保安装了必要的Python库。…...

【大模型】RankRAG:基于大模型的上下文排序与检索增强生成的统一框架

文章目录 A 论文出处B 背景B.1 背景介绍B.2 问题提出B.3 创新点 C 模型结构C.1 指令微调阶段C.2 排名与生成的总和指令微调阶段C.3 RankRAG推理&#xff1a;检索-重排-生成 D 实验设计E 个人总结 A 论文出处 论文题目&#xff1a;RankRAG&#xff1a;Unifying Context Ranking…...