文章目录
红黑树(R-B Tree),是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。AVL树是高度平衡的。C++ STL中,很多部分(包括set, multiset, map, multimap)的底层都是红黑树。对于任意一棵红黑树,它可以在O(
l
o
g
2
N
log_2N
log2N)时间内做查找,插入和删除,这里的N是树中元素的数目。
红黑树是每个节点都带有颜色属性的二叉查找树,颜色或红色或黑色。在二叉查找树强制一般要求以外,对于任何有效的红黑树还增加了如下的额外要求:
每个结点不是红色就是黑色根节点是黑色的 每个叶子结点都是黑色的(此处的叶子结点指的是空结点(NIL))如果一个节点是红色的,则它的两个孩子结点是黑色的对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点性质1和性质2:这个很好理解,红黑树嘛,结点不是红色就是黑色,至于为什么根结点是黑色的,因为这也是规定。
性质3:这里的叶子结点不是我们平常认知的叶子结点(没有子结点的结点),而是NIL(可以认为是nullptr)
性质4:在一棵红黑树中,不可能存在连续的两个红结点(就是父结点是红色的并且子结点也是红色的)
性质5:在上图中,例如黑结点13到黑结点1左边的NUL,有2个黑结点,到黑结点11的左右两边也有2个黑结点。对于红结点17到红结点22左右两边和到27的左右两边的黑结点数也是相同的,都为2。

因为上图满足这5点性质,所以该树是一棵红黑树
在R-B Tree的概念中提到红黑树是接近平衡的,因为红黑树确保没有一条路径会比其他路径长出俩倍。为什么是这样呢?
由于每个结点非红即黑(性质1),加上不存在两个连续的红结点(性质4),那么红黑树的最长路径一定是红黑交替的,而最短路径一定全都是黑结点。而任意结点其所有后代叶结点的简单路径上,均包含相同数目的黑色结点,则说明最长路径和最短路径中的黑色结点数目一定相等。最后加上根节点为黑(性质2)和叶子结点为黑(性质3),那么一定可以得到一个结论:最长路径<=2*最短路径

最长路径 = 最短路径

最长路径 = 2*最短路径

当我们在对红黑树进行插入操作时,对树做了修改,那么可能会违背红黑树的性质。为了保持红黑树的性质,我们可以通过对树进行旋转,即修改树种某些结点的颜色及指针结构,以达到对红黑树进行插入、删除结点等操作时,红黑树依然能保持它特有的性质(五点性质)。
红黑树的插入分为5种情况,在了解之前,我们还要必须知道插入的结点是红色的,如果插入结点为黑色,总是会破坏性质5,而插入红结点不一定会破坏性质5。因为在插入之前,该树已经是红黑树,插入了黑结点肯定会影响该结点到根结点的这条路径。比如:在上图中黑结点11的左边插入黑结点,除了这条路径的黑结点数为4之外,其他的都为3。那为什么插入红色结点不一定会破坏性质5呢?假如还是在上图中黑结点11的左边插入红结点,在插入之后,每条路径的黑结点还是3。
除此之外,我们将需要插入的结点标为N,父结点为P,祖父结点为G,叔结点为U(父亲的亲兄弟)。下面将逐一介绍这5种情况:
该树为空树,直接插入根结点的位置,违反性质1,把节点颜色有红改为黑即可

插入节点N的父节点P为黑色,不违反任何性质,无需做任何修改 
N为红,P为红,(祖节点一定存在,且为黑,下边同理)U也为红,这里不论P是G的左孩子,还是右孩子;不论N是P的左孩子,还是右孩子
为什么在这个情况下,祖父结点一定存在?因为P结点为红,假设祖父结点不存在,那么P将成为整颗树的根,但P为红色,已经违反了性质2(根结点为黑色)
在此情况下,只需要将P和U变色即可

N为红,P为红,U为黑(存在为黑或者不存在),P为G的左孩子,N为P的左孩子(或者P为G的右孩子,N为P的左孩子;反正就是同向的)
在此情况下,需要将P、G变色,P、G变换即左单旋(或者右单旋)
此处为左单旋,右单旋为相反操作

N为红,P为红,U为黑(存在为黑或者不存在),P为G的左孩子,N为P的右孩子(或者P为G的右孩子,N为P的左孩子;反正两方向相反)
在此情况下,需要进行两次旋旋转,P、N右单旋,G、N左单旋
此处为右左单旋,左右单旋为相反操作

红黑树的旋转和AVL树的旋转一样,只是红黑树没有平衡因子,不需要控制平衡因子
void RotateL(Node* parent)
{
Node* subR = parent->_right;//parent的的右边一定不为nullptr
Node* subRL = subR->_left;//subR的左边可能为nullptr
Node* pparent = parent->_parent;//保存parent的根(父节点)
//将subRL链接到parent的右侧
parent->_right = subRL;
if (subRL != nullptr)
subRL->_parent = parent;
//将parent链接到subR左侧
subR->_left = parent;
parent->_parent = subR;
//将subR和pparent链接起来
//pparent为nullptr,subR要成为新的根
if (pparent == nullptr)//_parent == _root
{
subR->_parent = pparent;
_root = subR;
}
//pparent不为nullptr,就链接pparent和subR
else
{
if (pparent->_right == parent)
pparent->_right = subR;
else
pparent->_left = subR;
subR->_parent = pparent;
}
}
void RotateR(Node* parent)
{
Node* subL = parent->_left;//parent的的左边一定不为nullptr
Node* subLR = subL->_right;//subL的右边可能为nullptr
Node* pparent = parent->_parent;//保存parent的根(父节点)
//将subLR链接到parent的左侧
parent->_left = subLR;
if (subLR != nullptr)
subLR->_parent = parent;
//将parent链接到subL右侧
subL->_right = parent;
parent->_parent = subL;
//将subL和pparent链接起来
//pparent为nullptr,subL要成为新的根
if (pparent == nullptr)
{
subL->_parent = pparent;
_root = subL;
}
//pparent不为nullptr,就链接pparent和subL
else
{
if (pparent->_left == parent)
pparent->_left = subL;
else
pparent->_right = subL;
subL->_parent = pparent;
}
}
//右旋左旋
void RotateRL(Node* parent)
{
RotateR(parent->_right);
RotateL(parent);
}
//左旋右旋
void RotateLR(Node* parent)
{
RotateL(parent->_left);
RotateR(parent);
}
#pragma once
#include<iostream>
using namespace std;
enum Color
{
RED,
BLACK
};
template<class K, class V>
struct RBTreeNode
{
RBTreeNode<K, V>* _parent;
RBTreeNode<K, V>* _left;
RBTreeNode<K, V>* _right;
pair<K, V> _kv;
Color _cor;
RBTreeNode(const pair<K, V>& kv)
:_parent(nullptr)
, _left(nullptr)
, _right(nullptr)
, _kv(kv)
, _cor(RED)
{}
};
template<class K, class V>
class RBTree
{
typedef RBTreeNode<K, V> Node;
public:
RBTree()
:_root(nullptr)
{}
bool Insert(const pair<K, V>& kv)
{
//_root为nullptr,说明是插入的第一个值
if (_root == nullptr)
{
_root = new Node(kv);
_root->_cor = BLACK;
return true;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
//插入的值比cur大,说明要在右边插入
if (kv.first > cur->_kv.first)
{
parent = cur;
cur = cur->_right;
}
//插入的值比cur小,说明要在左边插入
else if (kv.first < cur->_kv.first)
{
parent = cur;
cur = cur->_left;
}
else
return false;
}
//链接父子结点
cur = new Node(kv);
if (kv.first > parent->_kv.first)
{
parent->_right = cur;
cur->_parent = parent;
}
else
{
parent->_left = cur;
cur->_parent = parent;
}
//控制平衡
while (parent != nullptr && parent->_cor == RED)
{
//不需要判断grandfather是否为nullptr,因为parent如果是红色,不可能是根
Node* grandfather = parent->_parent;
if (parent == grandfather->_left)
{
Node* uncle = grandfather->_right;
//情况1、uncle存在且为红,不需要旋转处理
if (uncle != nullptr && uncle->_cor == RED)
{
//变色+向上继续调整
parent->_cor = uncle->_cor = BLACK;
grandfather->_cor = RED;
cur = grandfather;
parent = cur->_parent;
}
//情况2、uncle存在且为黑/不存在
else
{
//cur为parent的左孩子,需要进右单旋
// g p(黑)
// p c g(红)
// c
if (cur == parent->_left)
{
RotateR(grandfather);
parent->_cor = BLACK;
grandfather->_cor = RED;
}
//cur为parent的右孩子,需要左右双旋(先左旋,再右旋)
// g c(黑)
// p(红) p g(红)
// c(红)
else
{
RotateLR(grandfather);
//RorateL(parent);
//RorateR(grandfather);
cur->_cor = BLACK;
grandfather->_cor = RED;
}
break;
}
}
//parent为grandfather的右孩子,和parent为grandfather的左孩子的情况一样,只是方向不同
else
{
Node* uncle = grandfather->_left;
//情况1、uncle存在且为红,不需要旋转处理
if (uncle != nullptr && uncle->_cor == RED)
{
//变色+向上继续调整
parent->_cor = uncle->_cor = BLACK;
grandfather->_cor = RED;
cur = grandfather;
parent = cur->_parent;
}
//情况2、uncle存在且为黑/不存在
else
{
//cur为parent的右孩子,需要进左单旋
if (cur == parent->_right)
{
RotateL(grandfather);
parent->_cor = BLACK;
grandfather->_cor = RED;
}
//cur为parent的左孩子,需要进右左单旋
else
{
RotateRL(grandfather);
//RorateR(parent);
//RorateL(grandfather);
cur->_cor = BLACK;
grandfather->_cor = RED;
}
break;
}
}
}
//让根变黑
_root->_cor = BLACK;
return true;
}
private:
Node* _root;
//右单旋
void RotateR(Node* parent)
{
Node* subL = parent->_left;//parent的的左边一定不为nullptr
Node* subLR = subL->_right;//subL的右边可能为nullptr
Node* pparent = parent->_parent;//保存parent的根(父节点)
//将subLR链接到parent的左侧
parent->_left = subLR;
if (subLR != nullptr)
subLR->_parent = parent;
//将parent链接到subL右侧
subL->_right = parent;
parent->_parent = subL;
//将subL和pparent链接起来
//pparent为nullptr,subL要成为新的根
if (pparent == nullptr)
{
subL->_parent = pparent;
_root = subL;
}
//pparent不为nullptr,就链接pparent和subL
else
{
if (pparent->_left == parent)
pparent->_left = subL;
else
pparent->_right = subL;
subL->_parent = pparent;
}
}
//左单旋
void RotateL(Node* parent)
{
Node* subR = parent->_right;//parent的的右边一定不为nullptr
Node* subRL = subR->_left;//subR的左边可能为nullptr
Node* pparent = parent->_parent;//保存parent的根(父节点)
//将subRL链接到parent的右侧
parent->_right = subRL;
if (subRL != nullptr)
subRL->_parent = parent;
//将parent链接到subR左侧
subR->_left = parent;
parent->_parent = subR;
//将subR和pparent链接起来
//pparent为nullptr,subR要成为新的根
if (pparent == nullptr)//_parent == _root
{
subR->_parent = pparent;
_root = subR;
}
//pparent不为nullptr,就链接pparent和subR
else
{
if (pparent->_right == parent)
pparent->_right = subR;
else
pparent->_left = subR;
subR->_parent = pparent;
}
}
//右旋左旋
void RotateRL(Node* parent)
{
RotateR(parent->_right);
RotateL(parent);
}
//左旋右旋
void RotateLR(Node* parent)
{
RotateL(parent->_left);
RotateR(parent);
}
};
我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳
给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最
我正在尝试使用Curbgem执行以下POST以解析云curl-XPOST\-H"X-Parse-Application-Id:PARSE_APP_ID"\-H"X-Parse-REST-API-Key:PARSE_API_KEY"\-H"Content-Type:image/jpeg"\--data-binary'@myPicture.jpg'\https://api.parse.com/1/files/pic.jpg用这个:curl=Curl::Easy.new("https://api.parse.com/1/files/lion.jpg")curl.multipart_form_
无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD
本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01 客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02 数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit
文章目录一、概述简介原理模块二、配置Mysql使用版本环境要求1.操作系统2.mysql要求三、配置canal-server离线下载在线下载上传解压修改配置单机配置集群配置分库分表配置1.修改全局配置2.实例配置垂直分库水平分库3.修改group-instance.xml4.启动监听四、配置canal-adapter1修改启动配置2配置映射文件3启动ES数据同步查询所有订阅同步数据同步开关启动4.验证五、配置canal-admin一、概述简介canal是Alibaba旗下的一款开源项目,Java开发。基于数据库增量日志解析,提供增量数据订阅&消费。Git地址:https://github.co
HashMap中为什么引入红黑树,而不是AVL树呢1.概述开始学习这个知识点之前我们需要知道,在JDK1.8以及之前,针对HashMap有什么不同。JDK1.7的时候,HashMap的底层实现是数组+链表JDK1.8的时候,HashMap的底层实现是数组+链表+红黑树我们要思考一个问题,为什么要从链表转为红黑树呢。首先先让我们了解下链表有什么不好???2.链表上述的截图其实就是链表的结构,我们来看下链表的增删改查的时间复杂度增:因为链表不是线性结构,所以每次添加的时候,只需要移动一个节点,所以可以理解为复杂度是N(1)删:算法时间复杂度跟增保持一致查:既然是非线性结构,所以查询某一个节点的时候
我正在尝试在Rails上安装ruby,到目前为止一切都已安装,但是当我尝试使用rakedb:create创建数据库时,我收到一个奇怪的错误:dyld:lazysymbolbindingfailed:Symbolnotfound:_mysql_get_client_infoReferencedfrom:/Library/Ruby/Gems/1.8/gems/mysql2-0.3.11/lib/mysql2/mysql2.bundleExpectedin:flatnamespacedyld:Symbolnotfound:_mysql_get_client_infoReferencedf