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

超详细红黑树的模拟实现

在这里插入图片描述

前言

有人说设计出AVL树的的人是个大牛,那写红黑树(RBTree)的人就是天才!
上一篇文章,我们已经学习了AVL树,牛牛个人认为AVL树已经够优秀了,那让我们一起探究一下,为什么红黑树比AVL树的结构还要优秀吧!

目录

  • 前言
  • 一、红黑树的介绍
  • 二、手撕红黑树
    • 2.1 框架结构分析
      • 2.11 结点颜色
      • 2.12 结点类
      • 2.13 红黑树结构
    • 2.2 接口实现
      • 2.21 插入接口(重点)
        • 情况1: 父亲是爷爷的左,cur结点是父亲的左。 (左左)
        • 情况2: 父亲是爷爷的左,cur结点是父亲的右。 (左右)
        • 情况3: 父亲是爷爷的右,cur结点是父亲的左。 (右左)
        • 情况4: 父亲是爷爷的右,cur结点是父亲的左。 (右右)
      • 2.22 最左侧结点(LeftMost)
      • 2.23 最右侧结点(RightMost)
      • 2.24 检测函数(次重点)
      • 2.25 获取根节点
      • 2.25 获取红黑树的高度
      • 2.26 find函数
  • 三、结语:

一、红黑树的介绍

红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是RedBlack。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。

红黑树,是一种自平衡的二叉查找树,它的性质比较复杂,但却非常重要,常用于C++中的STL库中的setmap等容器。红黑树的节点有两种颜色:红色(red)和黑色(black)。它具有如下五个性质:

  1. 每个节点是红色或者黑色的。
  2. 根节点是黑色的。
  3. 每个叶子节点(这里特指最下面的空节点)是黑色的。
  4. 如果一个节点是红色的,则它的子节点必须是黑色的。(即:每条路径上不能出现连续的红结点)
  5. 从任一节点到其每个叶子节点的所有路径都包含相同数目的黑色节点。

由于红色结点的父亲必须是黑色结点,并且每条路径上的黑色结点的个数也必须相同,所以得到了红黑树最长路径中节点个数不会超过最短路径节点个数的两倍

这也就决定了,红黑树的高度是log(n)级别的。
例如,下面这个就是红黑树

在这里插入图片描述

二、手撕红黑树

2.1 框架结构分析

2.11 结点颜色

红黑树较于AVL树,不在使用平衡因子,而是增设了颜色变量,这里我们可以枚举出这两种颜色,方便使用。

	enum Colour	//枚举出颜色{RED,		//红色BLACK		//黑色};

2.12 结点类

同AVL树一样,红黑树也是三叉链

	//结点类template<class K, class V>struct RBTreeNode{//指针域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)				//注意这里,默认新构造的结点是红色的{}};

2.13 红黑树结构

	//红黑树的结构template<class K, class V>class RBTree{typedef RBTreeNode<K, V> Node;public:// 在红黑树中插入值为data的节点,插入成功返回true,否则返回false//此版本红黑树对于重复元素,插入失败bool Insert(const pair<K, V>& kv);// 搜索红黑树中是否存在值为data的节点。//存在返回该节点的地址,不存在则返回nullptrNode* Find(const pair<K, V>& data);// 获取红黑树最左侧节点Node* LeftMost();// 获取红黑树最右侧节点Node* RightMost();//(这里的玩法大家应该不陌生了)	int Height();//计算红黑树的高度bool IsValidRBTRee();// 检测红黑树是否为有效的红黑树private:bool _IsValidRBTRee(Node* pRoot, size_t blackCount, size_t pathBlack);int Height(Node* root);// 左单旋void RotateL(Node* pParent);// 右单旋void RotateR(Node* pParent);// 为了操作树简单起见:获取根节点Node*& GetRoot();private:Node* _root = nullptr;};

2.2 接口实现

2.21 插入接口(重点)

本篇主要讲解的部分就是红黑树的插入操作。

函数名 :insert
返回值插入成功,返回true;插入失败,返回false
形参键值对
//插入函数
template<class K, class V>
bool RBTree<K, V>::Insert(const pair<K, V>& kv) {
}

(1).如果是第一次插入,则插入的是根节点,则需要特殊处理,因为要给根节点root赋值。

在结点类中我们提到,在创建的新节点我们给与了默认颜色RED(红色),而红黑树的根节点必须是BLACK(黑色)的,这里一定要记得修改一下颜色。

//第一次插入if (_root == nullptr) {_root = new Node(kv);_root->_Col = BLACK;		//注意根节点一定是黑色的,默认构造的新节点是红色的,所以这里要改一下。return true;}

(2) 寻找插入位置
红黑树也是二叉搜索树,学到这里,相信友友们在AVL树和二叉搜索树学习阶段,已经知道如何寻找插入位置。

	//寻找插入位置while (cur) {parent = cur;if (_root->_kv.first > kv.first) {cur = cur->_left;		//插入的值当前结点的值小,往左走}else if (_root->_kv.first < kv.first) {cur = cur->_right;		//插入的值当前结点的值大,往右走}else {return false;	//本篇实现的红黑树,对于重复值,插入失败}}//判断插入在左边还是右边cur = new Node(kv);if (kv.first < parent->_kv.first) {				//插入在左边parent->_left = cur;}else {									//插入在右边parent->_right = cur;}cur->_parent = parent;					//保证三叉链的关系

3.看uncle(叔叔)

叔叔(uncle)?这里我将当前结点的父亲(parent)的兄弟称为叔叔结点。

示例:
在这里插入图片描述
当我们新增一个结点时,默认新节点的颜色为RED,如果它的父亲结点是黑色的,则不需要做任何调整,直接插入成功!

在这里插入图片描述
当父亲结点是红色的时候,则与新增结点一起,会构成连续的红色结点,此时需要调整。

调整规则主要看uncle叔叔结点。

情况1: 父亲是爷爷的左,cur结点是父亲的左。 (左左)

👻情况:叔叔存在且为红
🔑调整方案: 变色+向上更新在这里插入图片描述(图片为博主原创,请勿随意转发使用)

👻情况:叔叔不存在,或者存在且为黑
🔑调整方案: 右旋+变色

在这里插入图片描述

在这里插入图片描述(图片为博主原创,请勿随意转发使用)

情况2: 父亲是爷爷的左,cur结点是父亲的右。 (左右)

👻情况:叔叔存在且为红
🔑调整方案: 变色+向上更新
在这里插入图片描述(图片为博主原创,请勿随意转发使用)

👻情况:叔叔不存在,或者存在且为黑
🔑调整方案: 左右双旋+变色

示例图:
在这里插入图片描述

未写(图片为博主原创,请勿随意转发使用)

情况3: 父亲是爷爷的右,cur结点是父亲的左。 (右左)

👻情况:叔叔存在且为红
🔑调整方案: 变色+向上更新
这里不画图了,牛牛画累了。

👻情况:叔叔不存在,或者存在且为黑
🔑调整方案: 右左双旋+变色
在这里插入图片描述

在这里插入图片描述(图片为博主原创,请勿随意转发使用)

情况4: 父亲是爷爷的右,cur结点是父亲的左。 (右右)

👻情况:叔叔存在且为红
🔑调整方案: 变色+向上更新
在这里插入图片描述(图片为博主原创,请勿随意转发使用)

👻情况:叔叔不存在,或者存在且为黑
🔑调整方案: 左旋+变色
在这里插入图片描述(图片为博主原创,请勿随意转发使用)

总结:
红黑树的插入主要看uncle
分为两种情况:
(1)uncle存在且为红
调整方案: 变色+继续向上调整
(2)uncle不存在或者uncle存在且为黑
调整方案: 旋转+变色

至于如何旋转,因为红黑树没有采用平衡因子的方式,所以我们采用判断grandfather与parent 和 parentcur的关系结构来决定。
下图是具体调整总结:
在这里插入图片描述

总代码:

bool Insert(const T& kv) {if (_root == nullptr) {_root = new Node(kv);_root->_Col = BLACK;		//注意根节点一定是黑色的,默认构造的新节点是红色的,所以这里要改一下。return true;}Node* cur = _root, * parent = nullptr;//寻找插入位置while (cur) {parent = cur;if (_root->_kv.first > kv.first) {cur = cur->_left;}else if (_root->_kv.first < kv.first) {cur = cur->_right;}else {return false;}}//判断插入在左边还是右边cur = new Node(kv);if (kv.first < parent->_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) {//变色uncle->_Col=parent->_Col = BLACK;grandfather->_Col = RED;//继续向上更新cur = grandfather;parent = grandfather->_parent;}else {									//叔叔不存在或者 存在且为黑if (cur == parent->_left) {			//			g//		p//cRotateR(grandfather);grandfather->_Col = RED;parent->_Col = BLACK;}else {//		g//	p//		cRotateL(parent);RotateR(grandfather);cur->_Col = BLACK;grandfather->_Col = RED;}break;	//此时最顶端的结点已经变成黑色了,不需要继续向上更新了。}}else {		// 如果父亲是爷爷的右孩子Node* uncle = grandfather->_left;		//那么叔叔就是爷爷的左孩子//叔叔存在且为红if (uncle && uncle->_Col == RED) {//变色uncle->_Col = parent->_Col = BLACK;grandfather->_Col = RED;//继续向上更新cur = grandfather;parent = grandfather->_parent;}else {									//叔叔不存在或者 存在且为黑if (cur == parent->_left) {//	g//		p//	c//注意旋转时的传参RotateR(parent);RotateL(grandfather);grandfather->_Col = RED;cur->_Col = BLACK;}else {//	g//		p//			cRotateL(grandfather);		//注意旋转时的传参parent->_Col = BLACK;grandfather->_Col = RED;}break;	//此时最顶端的结点已经变成黑色了,不需要继续向上更新了。}}}_root->_Col = BLACK;		//最后根节点一定是黑的return true;}

2.22 最左侧结点(LeftMost)

对于二叉搜索树,如果我们按中序遍历,则可以得到一个有序序列。
中序遍历的首个结点: 最左侧结点
中序遍历的最后结点: 最右侧结点

在这里插入图片描述

	// 获取红黑树最左侧节点template<class K, class V>typename RBTree<K, V>::Node* RBTree<K, V>::LeftMost() {Node* left_most = _root;while (left_most->left) {left_most = left_most->left;}return left_most;}

2.23 最右侧结点(RightMost)

	// 获取红黑树最右侧节点template<class K, class V>typename RBTree<K, V>::Node* RBTree<K, V>::RightMost() {Node* right_most = _root;while (right_most->right) {right_most = right_most->left;}return right_most;}

2.24 检测函数(次重点)

在实现红黑树时,也许我们会遇到各种问题,好不容易跑通代码后,我们缺无法判断自己实现的红黑树是否正确,是否符合红黑树的规则。

此时,我们可以设计一个检测函数,检测实现的红黑树是否平衡。

  1. 空树也是红黑树
  2. 根节点必须是红黑树
  3. 我们可以设置一个“基准值”,基准值为红黑树一条路径中的黑色结点的个数。
  4. 遍历每条红黑树的路径,判断红黑树结点的个数,是否与基准值相等。
  5. 除此之外,出现连续两个红色结点则返回false
// 检测红黑树是否为有效的红黑树,注意:其内部主要依靠_IsValidRBTRee函数检测
template<class K, class V>
bool  RBTree<K, V>::IsValidRBTRee() {if (_root == nullptr)	//空树也是红黑树return true;if (_root->_Col != BLACK)	//根节点必须是红黑树{return false;}// 基准值int pathBlack = 0;Node* cur = _root;while (cur){if (cur->_Col == BLACK)++pathBlack;cur = cur->_left;}_IsValidRBTRee(_root, 0, pathBlack);
}template<class K, class V>
bool RBTree<K, V>::_IsValidRBTRee(Node* pRoot, size_t blackCount, size_t pathBlack) {if (pRoot == nullptr){if (blackCount != pathBlack)		//一条路径走到底,也就是走到叶子结点以后,判断这条路径上的黑色结点个数(blackCount)是否与 设定的黑色结点个数相同(pathBlack)return false;return true;}if (pRoot->_Col == BLACK){++blackCount;}if (pRoot->_Col == RED && pRoot->_parent && pRoot->_parent->_Col == RED){cout << _root->_kv.first << "出现连续红色节点" << endl;return false;}//递归访问左右子树return _IsValidRBTRee(pRoot->_left, blackCount, pathBlack)&& _IsValidRBTRee(pRoot->_right, blackCount, pathBlack);
}

2.25 获取根节点

	template<class K, class V>typename RBTree<K, V>::Node*& RBTree<K, V>::GetRoot() {return _root;}

2.25 获取红黑树的高度

	template<class K, class V>int RBTree<K, V>::Height(){return Height(_root);}template<class K, class V>int RBTree<K, V>::Height(typename RBTree<K, V>::Node* root){if (root == nullptr)return 0;int leftHeight = Height(root->_left);	//计算左子树的高度int rightHeight = Height(root->_right);	//计算右子树的高度return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;}

2.26 find函数

template<class K, class T,>
typename RBTree<class K, class T, class Of_T>::Node* RBTree<class K, class T, class Of_T>::Find(const T& data) {Node* cur = _root;while (cur){if (data > cur->_data){cur = cur->_right;}else if (data < cur->_data){cur = cur->_left;}else return cur;}return nullptr;  // 找不到目标元素时返回nullptr
}

三、结语:

看完本篇文章,我们不难知道,对于插入操作,无论是红黑树还是avl树,要维持对应的“平衡”,会进行沿路径的更新,其中涉及大量的旋转操作,而红黑树较于avl树那种严格的高度差在-11之间,红黑树的平衡条件相对宽松,这也就大大减少了的为了维持平衡的大量旋转操作,而且还能保证效率在log(N),这也就是为啥说红黑树较于avl树更加优秀。
你赞同这个观点吗?
在这里插入图片描述

后续牛牛会模拟实现mapset,会在那篇文章封装红黑树,对红黑树进行改造,增加迭代器等功能。帮助友友们更加深入理解mapset容器。

在这里插入图片描述

相关文章:

超详细红黑树的模拟实现

前言 有人说设计出AVL树的的人是个大牛&#xff0c;那写红黑树&#xff08;RBTree&#xff09;的人就是天才&#xff01; 上一篇文章&#xff0c;我们已经学习了AVL树&#xff0c;牛牛个人认为AVL树已经够优秀了&#xff0c;那让我们一起探究一下&#xff0c;为什么红黑树比AV…...

【亚马逊云科技】通过Amazon CloudFront(CDN)快速访问资源

文章目录 前言一、应用场景二、【亚马逊云科技】CloudFront&#xff08;CDN&#xff09;的优势三、入门使用总结 前言 前面有篇文章我们介绍了亚马逊云科技的云存储服务。云存储服务主要用于托管资源&#xff0c;而本篇文章要介绍的CDN则是一种对托管资源的快速访问服务&#…...

ES-ES的基本概念

ES的基本概念 一、文档 1.1 文档相关概念 ES是面向文档的&#xff0c;文档是所有可搜索数据的最小单位&#xff0c;可以对比理解为关系型数据库中的一条数据 日志文件中的一条日志信息一本电影的具体信息/一张唱片的详细信息 文档会被序列化成JSON格式保存在ES中 JSON对象由…...

排序算法——快速排序的非递归写法

快速排序的非递归 我们写快速排序的时候&#xff0c;通常用的递归的方法实现快速排序&#xff0c;那么有没有非递归的方法实现快速排序呢&#xff1f;肯定是有的。思想还是一样的&#xff0c;不过非递归是看似是非递归其实还是递归。 思路解释 快速排序的非递归使用的是栈这…...

【论文阅读】基于人工智能目标检测与跟踪技术的过冷流沸腾气泡特征提取

Bubble feature extraction in subcooled flow boiling using AI-based object detection and tracking techniques 基于人工智能目标检测与跟踪技术的过冷流沸腾气泡特征提取 期刊信息&#xff1a;International Journal of Heat and Mass Transfer 2024 级别&#xff1a;EI检…...

RabbitMQ讲解与整合

RabbitMq安装 类型概念 租户 RabbitMQ 中有一个概念叫做多租户&#xff0c;每一个 RabbitMQ 服务器都能创建出许多虚拟的消息服务器&#xff0c;这些虚拟的消息服务器就是我们所说的虚拟主机&#xff08;virtual host&#xff09;&#xff0c;一般简称为 vhost。 每一个 vhos…...

python 基础知识点(蓝桥杯python科目个人复习计划56)

今日复习内容&#xff1a;做题 例题1&#xff1a;最小的或运算 问题描述&#xff1a;给定整数a,b&#xff0c;求最小的整数x&#xff0c;满足a|x b|x&#xff0c;其中|表示或运算。 输入格式&#xff1a; 第一行包括两个正整数a&#xff0c;b&#xff1b; 输出格式&#…...

【vue】vue中数据双向绑定原理/响应式原理,mvvm,mvc、mvp分别是什么

关于 vue 的原理主要有两个重要内容&#xff0c;分别是 mvvm 数据双向绑定原理&#xff0c;和 响应式原理 MVC&#xff08;Model-View-Controller&#xff09;&#xff1a; Model&#xff08;模型&#xff09;&#xff1a;表示应用程序的数据和业务逻辑。View&#xff08;视图&…...

基于反光柱特征的激光定位算法思路

目录 1. 识别反光柱2. 数据关联2.1 基于几何形状寻找匹配2.2 暴力寻找匹配 3. 位姿估计&#xff08;最小二乘求解&#xff09;4. 问题4.1 精度问题4.2 快速旋转时定位较差 1. 识别反光柱 反光柱是特殊材料制成&#xff0c;根据激光雷达对反光材料扫描得到的反射值来提取特征。…...

CSM是什么意思?

CSM(Customer Service Management)是企业客户服务管理的信息化&#xff08;IT&#xff09;解决方案架构。本着以客户为中心的管理理念&#xff0c;搭建企业客户服务管理平台&#xff0c;实现企业以客户为中心的管理时代的竞争战略。 CSM的核心是以客户为中心&#xff0c;实现对…...

ES6 面试题

1. const、let 和 var 的区别是什么&#xff1f; 答案&#xff1a; var 声明的变量是函数作用域或全局作用域&#xff0c;而 const 和 let 声明的变量是块级作用域。使用 var 声明的变量可以被重复声明&#xff0c;而 const 和 let 不允许重复声明同一变量。const 声明的变量…...

智能指针(C++)

目录 一、智能指针是什么 二、为什么需要智能指针 三、智能指针的使用和原理 3.1、RALL 3.2 智能指针的原理 3.3、智能指针的分类 3.3.1、auto_ptr 3.3.2、unique_ptr 3.3.3、shared_ptr 3.2.4、weak_ptr 一、智能指针是什么 在c中&#xff0c;动态内存的管理式通过一…...

社区店商业模式探讨:如何创新并持续盈利?

在竞争激烈的商业环境中&#xff0c;社区店要想获得成功并持续盈利&#xff0c;需要不断创新和优化商业模式。 作为一名开鲜奶吧5年的创业者&#xff0c;我将分享一些关于社区店商业模式创新的干货和见解&#xff0c;希望能给想开实体店或创业的朋友们提供有价值的参考。 1、…...

一些可以访问gpt的方式

1、Coze扣子是新一代 AI 大模型智能体开发平台。整合了插件、长短期记忆、工作流、卡片等丰富能力&#xff0c;扣子能帮你低门槛、快速搭建个性化或具备商业价值的智能体&#xff0c;并发布到豆包、飞书等各个平台。https://www.coze.cn/ 2、https://poe.com/ 3、插件阿里…...

springer模板参考文献不显示

Spring期刊模板网站&#xff0c;我的问题是23年12月的版本 https://www.springernature.com/gp/authors/campaigns/latex-author-support/see-where-our-services-will-take-you/18782940 参考文献显示问好&#xff0c;在sn-article.tex文件中&#xff0c;这个sn-mathphys-num…...

【【C语言简单小题学习-1】】

实现九九乘法表 // 输出乘法口诀表 int main() {int i 0;int j 0;for (i 1; i < 9; i){for (j 1; j < i;j)printf("%d*%d%d ", i , j, i*j);printf("\n"); }return 0; }猜数字的游戏设计 #define _CRT_SECURE_NO_WARNINGS 1 #include<stdi…...

mongoDB 优化(1)索引

1、创建复合索引&#xff08;多字段&#xff09; db.collection_test1.createIndex({deletedVersion: 1,param: 1,qrYearMonth: 1},{name: "deletedVersion_1_param_1_qrYearMonth_1",background: true} ); 2、新增索引前&#xff1a; 执行查询&#xff1a; mb.r…...

stable diffusion webUI之赛博菩萨【秋葉】——工具包新手安裝与使用教程

stable diffusion webUI之赛博菩萨【秋葉】——工具包新手安裝与使用教程 AI浪潮袭来&#xff0c;还是学习学习为妙赛博菩萨【秋葉】简介——&#xff08;葉ye&#xff0c;四声&#xff0c;同叶&#xff09;A绘世启动器.exe&#xff08;sd-webui-aki-v4.6.x&#xff09;工具包安…...

鸿蒙应用程序包安装和卸载流程

开发者 开发者可以通过调试命令进行应用的安装和卸载&#xff0c;可参考多HAP的调试流程。 图1 应用程序包安装和卸载流程&#xff08;开发者&#xff09; 多HAP的开发调试与发布部署流程 多HAP的开发调试与发布部署流程如下图所示。 图1 多HAP的开发调试与发布部署流程 …...

C语言数组全面解析:从初学到精通

数组 1. 前言2. 一维数组的创建和初始化3. 一维数组的使用4. 一维数组在内存中的存储5. 二维数组的创建和初始化6. 二维数组的使用7. 二维数组在内存中的存储8. 数组越界9. 数组作为函数参数10. 综合练习10.1 用函数初始化&#xff0c;逆置&#xff0c;打印整型数组10.2 交换两…...

基于服务器使用 apt 安装、配置 Nginx

&#x1f9fe; 一、查看可安装的 Nginx 版本 首先&#xff0c;你可以运行以下命令查看可用版本&#xff1a; apt-cache madison nginx-core输出示例&#xff1a; nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...

Python爬虫实战:研究feedparser库相关技术

1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...

华为OD机考-机房布局

import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...

VisualXML全新升级 | 新增数据库编辑功能

VisualXML是一个功能强大的网络总线设计工具&#xff0c;专注于简化汽车电子系统中复杂的网络数据设计操作。它支持多种主流总线网络格式的数据编辑&#xff08;如DBC、LDF、ARXML、HEX等&#xff09;&#xff0c;并能够基于Excel表格的方式生成和转换多种数据库文件。由此&…...

Unity VR/MR开发-VR开发与传统3D开发的差异

视频讲解链接&#xff1a;【XR马斯维】VR/MR开发与传统3D开发的差异【UnityVR/MR开发教程--入门】_哔哩哔哩_bilibili...

Matlab实现任意伪彩色图像可视化显示

Matlab实现任意伪彩色图像可视化显示 1、灰度原始图像2、RGB彩色原始图像 在科研研究中&#xff0c;如何展示好看的实验结果图像非常重要&#xff01;&#xff01;&#xff01; 1、灰度原始图像 灰度图像每个像素点只有一个数值&#xff0c;代表该点的​​亮度&#xff08;或…...

字符串哈希+KMP

P10468 兔子与兔子 #include<bits/stdc.h> using namespace std; typedef unsigned long long ull; const int N 1000010; ull a[N], pw[N]; int n; ull gethash(int l, int r){return a[r] - a[l - 1] * pw[r - l 1]; } signed main(){ios::sync_with_stdio(false), …...

OpenGL-什么是软OpenGL/软渲染/软光栅?

‌软OpenGL&#xff08;Software OpenGL&#xff09;‌或者软渲染指完全通过CPU模拟实现的OpenGL渲染方式&#xff08;包括几何处理、光栅化、着色等&#xff09;&#xff0c;不依赖GPU硬件加速。这种模式通常性能较低&#xff0c;但兼容性极强&#xff0c;常用于不支持硬件加速…...

Excel 怎么让透视表以正常Excel表格形式显示

目录 1、创建数据透视表 2、设计 》报表布局 》以表格形式显示 3、设计 》分类汇总 》不显示分类汇总 1、创建数据透视表 2、设计 》报表布局 》以表格形式显示 3、设计 》分类汇总 》不显示分类汇总...

第6章:Neo4j数据导入与导出

在实际应用中&#xff0c;数据的导入与导出是使用Neo4j的重要环节。无论是初始数据加载、系统迁移还是数据备份&#xff0c;都需要高效可靠的数据传输机制。本章将详细介绍Neo4j中的各种数据导入与导出方法&#xff0c;帮助读者掌握不同场景下的最佳实践。 6.1 数据导入策略 …...