AVL树实现,AVL树特点
Yyds干货库存
写之前要遇到C中最难的部分,也是一些大厂可能会出的面试题,比如手撕一颗红黑树。这篇博客的内容可能有点耗脑,所以我尽量接地气的分享给大家。
AVL树这种树也叫平衡二叉树。这是以前的二叉查找树的变体。前面说过,搜索二叉树对于更有序的元素来说有点深,甚至转换成单树,所以搜索效率有点低。所以我们提出AVL树。所谓平衡二叉树,就是在二叉查找树中插入新的节点时,如果保证每个节点的左右子树高度差的绝对值小于1(树中的节点需要调整),就可以降低树的高度,从而减少平均搜索长度。这是AVL树的定义。至于怎么做,我们这里不需要关心。
我们先来看看AVL树的特点。一般来说,对于那些比较有序的元素,我们通过一系列手段插入元素,降低树的高度,最终的树还是符合二叉查找树的特点的。
AVL树的实现我们已经知道AVL树是什么了。今天我们就来简单实现一下。这个过程说实话有点难。我们在这里实现KV模型。不过还是先把框架搭好吧。
命名空间位
{
模板类T,类T
AVLTree级
{
};
}平衡因素我们在前面设置了大框架,所以我们需要在这里开始构建树的节点。先看一段时间,再完善。我们已经在这里建立了,为什么还要加入父节点呢?等用上了再说吧。
结构AVLTreeNode
{
AVLTreeNode(const T data=T())
:_praent(nullptr)
,_left(nullptr)
,_right(nullptr)
,_data(数据)
{}
AVLTreeNode T * _ praent
AVLTreeNode T * _ left
AVLTreeNode T * _ right
T _ data
};现在我们需要谈一个问题。我们如何保证左右节点的差距只有一个?这里,我们需要使用一个平衡因子。父节点的平衡因子记录了左右子树的高度差。这样可以看到插入时平衡因子的变化,从而判断是否移动树。这里,我们的平衡因子记录了右边的子树减去左边的子树。至于如何修改平衡因子的值,这个就解释了。
模板类T
结构AVLTreeNode
{
AVLTreeNode(const T data=T())
:_praent(nullptr)
,_left(nullptr)
,_right(nullptr)
,_data(数据)
,_bf(0)
{}
AVLTreeNode T * _ praent
AVLTreeNode T * _ left
AVLTreeNode T * _ right
T _ data
int _ bf
};插入元素现在我们的大框架已经奠定,现在开始重点工作,比较烧脑。我们也只实现插入操作。
模板类T
结构AVLTreeNode
{
AVLTreeNode(const T data=T())
:_praent(nullptr)
,_left(nullptr)
,_right(nullptr)
,_data(数据)
,_bf(0)
{}
AVLTreeNode T * _ praent
AVLTreeNode T * _ left
AVLTreeNode T * _ right
T _ data
int _ bf
};
模板类别K,类别T
AVLTreeV级
{
公共:
typedef AVLTreeNode T节点;
AVLTree()
:_pRoot(nullptr)
{}
布尔插入(常量数据)
{
返回true
}
公共:
Node * _ pRoot
};第一步是搜索二叉树的简单操作。我们先找出插入的节点在哪里。
布尔插入(常量数据)
{
//第一步是判断第一次插入
if (_pRoot==nullptr)
{
_pRoot=新节点(数据);
_ pRoot-_ BF=0;
返回true
}
//步骤2搜索二叉树找到位置
Node * cur=_ pRoot
Node * parent=nullptr
而(cur!=nullptr)
{
if (cur- _data数据)
{
//去右边的树找
parent=cur
cur=cur-_ right;
}
else if (cur- _data数据)
{
//左树搜索
parent=cur
cur=cur-_ left;
}
其他
{
//我们这里不接受重复的值,你可以自己实现
返回false
}
}
//第三步:new带着节点出来
Node* node=新节点(数据);
node-_ BF=0;//记住不要相信构造函数,它可能不是你写的,也不会自动默认为0。
//步骤4确定插入左树还是右树。
if (parent- _data数据)
{
parent-_ left=node;
}
其他
{
parent-_ right=node;
}
//这里需要一个链接
node-_ parent=parent;
cur=节点;
//更新平衡因子
//.
返回true
}这里我们保留了最关键的一步,就是更新平衡因子。这里要讨论的东西太多了。首先我们要判断插入的节点是左树还是右树,方便我们修改父节点的平衡因子。
布尔插入(常量数据)
{
//第一步是判断第一次插入
if (_pRoot==nullptr)
{
_pRoot=新节点(数据);
_ pRoot-_ BF=0;
返回true
}
//步骤2搜索二叉树找到位置
Node * cur=_ pRoot
Node * parent=nullptr
而(cur!=nullptr)
{
if (cur- _data数据)
{
//去右边的树找
parent=cur
cur=cur-_ right;
}
else if (cur- _data数据)
{
//左树搜索
parent=cur
cur=cur-_ left;
}
其他
{
//我们这里不接受重复的值,你可以自己实现
返回false
}
}
//第三步:new带着节点出来
Node* node=新节点(数据);
node-_ BF=0;//记住不要相信构造函数,它可能不是你写的,也不会自动默认为0。
//步骤4确定插入左树还是右树。
if (parent- _data数据)
{
parent-_ left=node;
}
其他
{
parent-_ right=node;
}
//这里需要一个链接
node-_ parent=parent;
cur=节点;
//更新平衡因子
而(家长!=nullptr)
{
//如果插入了右边的子树
if (cur==parent- _right)
{
parent-_ BF;
}
其他
{
parent-_ BF-;
}
//开始检测
if (parent- _bf==0)
{
打破;
}
else if(parent-_ BF==1 parent-_ BF==-1)
{
cur=parent
parent=parent-_ parent;
}
else if(parent-_ BF==2 parent-_ BF==-2)
{
//这是大头。
if (cur- _bf==1 parent- _bf==2)
{
}
else if(cur-_ BF==-1 parent-_ BF==-2)
{
}
//.
}
其他
{
断言(假);
}
}
返回true
}前面我们简单描述了平衡因子的变化。这里,我们具体情况具体分析。首先,我们是按照祖先来更新的,这一点毋庸置疑。
if (cur==prev- _right)
{
prev-_ BF;
}
其他
{
prev-_ BF-;
}也就是说我们修改平衡因子,在我们的子树高度没有变化的时候,也就是平衡因子为0的时候,停止更新。如果高度变为1,那么右边子树的高度一定更高,这就需要我们不断往上走。如果-1是左树的高度,我们也应该往上走。这里重点是-2和2,这是我们要讲的重点,也是我们。
左旋假设右边的子树比左边的子树高两倍,它也满足一定的条件。让我们使用左旋转。我们先分析一下情况。
至于怎么轮换,暂且不谈。先说一些具体情况。
当h=0时
当h=1时
当h=2时,这里有很多情况。
我们就不回去分析了,下面是具体用法,还是按照理论来轮换。
将SURL赋予父的右边子树,将父赋予SURL的左边子树,以修改逻辑顺序和平衡因子。
if (cur- _bf==1 parent- _bf==2)
{
//左撇子
RotateL(父母);
}
void RotateL(节点*父节点)
{
node * subR=parent-_ right;
node * subRL=subR-_ left;
parent-_ right=subRL;
if(subRL)
subRL-_ praent=parent;
//记录祖父节点
node * grande=parent-_ praent;
parent-_ praent=subR;
subR-_ left=parent;
if(祖父母==nullptr)
{
//parent是根节点。
_ pRoot=subR
_ pRoot-_ praent=nullptr;
}
其他
{
subR- _praent=祖父母;
//在判断原始父节点和祖父节点之间的关系时
if(parent==grande-_ left)
{
祖父母-_ left=subL;
}
其他
{
祖父母-_ right=subL;
}
}
//修改平衡因子
subR-_ BF=0;
parent-_ BF=0;
}我们先分析一下特殊情况。subRL可能为空,即h=0,这就需要判断subRL何时修改父节点。还有,你不觉得我们太圆滑了吗?如何确定父节点一定是根节点?我们面前的模型是一个子树模型,不一定保证是一棵完整的树。这个问题还是要判断的,不然就有问题了。
正确的旋转相对简单。我们直接看这里的模型。正确的旋转是正确的高度,这里需要移到右边。
else if(cur-_ BF==-1 parent-_ BF==-2)
{
//右撇子
RotateR(父母);
打破;
}
空旋转器(节点*父节点)
{
node * subL=parent-_ left;
node * subLR=subL-_ right;
parent-_ left=subLR;
if (subLR)
subLR-_ parent=parent;
节点*祖父母=父母-_父母;
parent-_ parent=subL;
subL-_ right=parent;
if(祖父母==nullptr)
{
_ pRoot=subL
_ pRoot-_ parent=nullptr;
}
其他
{
subL- _parent=祖父母;
if(祖父母-_左==父母)
{
祖父母-_ left=subL;
}
其他
{
祖父母-_ right=subL;
}
}
//更新平衡因子
subL-_ BF=parent-_ BF=0;
}我们先测试一下单旋转,也就是我们尝试插入有序元素。这里,我们只需要一次旋转操作。但在这里,我们需要注意。这里不能确定我们的树是AVL树,只是看我们的代码是否符合逻辑。
void test1()
{
AVLTree int,int a;
for(int I=0;I 8;我)
{
a.插入(I 1);
}
Cout“序列遍历”endl
a . level order();
Cout“有序遍历”endl
a . in order();
}
左右旋转现在我们开始一个复杂的情况,就是双旋转的问题。如果直接插入8,6,7,就有意思了。看一看。你会发现简单的单一旋转是解决不了问题的。这里需要一些手段。
我们先看一下模型,然后再讨论解决方案。这里就像一个折线图。
这里我们也列举几个具体情况。
这个时候我们在想这个应该怎么旋转。我们需要一些灵活性。先看大政方针。
也就是说,我们需要先用subL做左单旋,再用parent做右单旋。
else if(cur-_ BF==1 parent-_ BF==-2)
{
RotateLR(parent);
打破;
}
void RotateLR(节点*父节点)
{
RotateL(parent-_ left);
RotateR(父母);
//修改平衡因子
//.
}注意我们的轮换没有问题。这里可以直接重用前面的,但难点是需要修改平衡因子。然后就有以下几个问题。看看下面的方法
我们需要保存这些节点,并讨论如何修改这些节点的平衡因子。
void RotateLR(节点*父节点)
{
node * subL=parent-_ left;
node * subLR=subL-_ right;
RotateL(parent-_ left);
RotateR(父母);
//修改平衡因子
//.
}现在我们剩下的问题是如何修改平衡因子。我们知道SULR一定是0,但是subL和parent不一定。这个需要具体情况具体分析,那么我们讨论的基础是什么呢?那很有趣。我们就按照subLR的平衡因子的值来讨论吧。但是我们需要先录下来,单转的时候会修改。我们按照下图修改一下就可以了。
void RotateLR(节点*父节点)
{
node * subL=parent-_ left;
node * subLR=subL-_ right;
int BF=subLR-_ BF;
RotateL(parent-_ left);
RotateR(父母);
//修改平衡因子
如果(bf==0)
{
parent-_ BF=0;
subL-_ BF=0;
subLR-_ BF=0;
}
else if (bf==1)
{
parent-_ BF=0;
subL-_ BF=-1;
subLR-_ BF=0;
}
else if (bf==-1)
{
parent-_ BF=1;
subL-_ BF=0;
subLR-_ BF=0;
}
其他
{
断言(假);
}
}左右双旋这个和上面一样,也是折线。我们需要左右单旋。
看解决方案,这里就不举具体例子了。
else if(cur-_ BF==-1 parent-_ BF==2)
{
rotater l(parent);
打破;
}
void rotator 1(节点*父节点)
{
node * subR=parent-_ right;
node * subRL=subR-_ left;
int BF=subRL-_ BF;
rotator(parent-_ right);
RotateL(父母);
//修改平衡因子
}我们还是把重点放在如何修改平衡因子上,需要简单分析一下,但思路同上。
void rotator 1(节点*父节点)
{
node * subR=parent-_ right;
node * subRL=subR-_ left;
int BF=subRL-_ BF;
rotator(parent-_ right);
RotateL(父母);
//修改平衡因子
如果(bf==0)
{
subR-_ BF=0;
parent-_ BF=0;
subRL-_ BF=0;
}
else if(bf==1)
{
subR-_ BF=0;
parent-_ BF=-1;
subRL-_ BF=0;
}
else if (bf==-1)
{
subR-_ BF=1;
parent-_ BF=0;
subRL-_ BF=0;
}
其他
{
断言(假);
}
为了避免代码错误,我们还在这里测试了无序度。
void test2()
{
int arr[]={ 1,4,3,7,5,8,1,10 };
int SZ=sizeof(arr)/sizeof(arr[0]);
AVLTree int,int a;
for(int I=0;我SZ;我)
{
a.insert(arr[I]);
}
Cout“序列遍历”endl
a . level order();
Cout“有序遍历”endl
a . in order();
}
AVL树的判定这里需要看看我们写的AVL树是否正确。在这里,我们可以直接上传代码。
公共:
bool isBalanceTree()
{
return _ is balance tree(_ pRoot);
}
私人:
int _height(节点*根)
{
if (root==nullptr)
返回0;
int LH=_ height(root-_ left);
int RH=_ height(root-_ right);
返回左侧右侧?LH 1:RH 1;
}
bool _IsBalanceTree(节点*根)
{
//空树也是平衡二叉树
if (root==nullptr)
返回true
//记录左右节点的高度
int left=_ height(root-_ left);
int right=_ height(root-_ right);
int diff=左-右;
//判断平衡因子
如果(绝对值(差值)=2)
{
Cout“平衡系数更新错误”endl
返回false
}
如果(右-左!=root- _bf)
{
Cout 节点平衡系数与实际不符 endl
返回false
}
return _ is balance tree(root-_ left)_ is balance tree(root-_ right);
让我们测试有序和无序插入。
无效测试4()
{
int N=1024
AVLTree int,int a;
for(int I=0;I N;我)
{
a.插入;
}
cout is it balanced a . is balance tree()endl;
}
无效测试5()
{
int N=1024 * 1024
AVLTree int,int a;
srand(time(0));
for(int I=0;I N;我)
{
a.insert(rand());
}
cout is it balanced a . is balance tree()endl;
}
红树说完AVL树,这里我们需要看一下红黑树。你不觉得我们的AVL树太严格了吗?我们总是移动子树。我们先来看看红黑树的定义。
红色的树是一个二叉查找树,但是每个节点都添加了一个存储位来指示节点的颜色,可以是红色或黑色。通过限制从根到叶的任意路径上每个节点的着色方法,红黑树保证了没有一条路径会比其他路径长一倍,所以近乎平衡。红黑树的搜索效率仍然低于AVL树,但综合效率并不差。
我们提取了两个基本观点。
节点颜色受颜色限制,保证没有一条路比其他路强一倍。
红树的特性我们需要分析红黑树的特性,以便后期实现。
每个节点不是红色就是黑色。根节点是黑色的。如果一个节点是红色的,它的两个子节点是黑色的,即红色是不连续的。对于每个节点,从该节点到其所有后代叶子的简单路径包含相同数量的黑色节点。每个叶子节点都是黑色的(这里叶子节点指的是空节点),弥补了特性3。首先,我们要分析一下为什么满足上述特征。这里我们可以看到一棵红黑树。首先,我们可以这样想。最短路径被假定为黑色。根据特性4,有相同的黑色节点,所以如果有最长的节点,就增加一些红色节点。根据特征3,红色节点不能连续,最长的大概是一红一黑。
在这里,我们需要看看红黑树的节点是如何设计的。
枚举颜色
{
红色,
黑色
};
模板类别K,类别V
结构RBTreeNode
{
RBTreeNode(常数对K,V kv)
:_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_kv(千伏)
,_col(红色)
{}
RBTreeNode K,V _ left
RBTreeNode K,V _ right
RBTreeNode K,V _ parent
对K,V _ kv
颜色_颜色;
};这里我们想知道为什么默认节点是红色的?我们可以这样想。如果我们是黑的,不管父节点是红的还是黑的,我们都不需要旋转,但是这样会导致我插入的元素的一个分支的黑色增加1,这就破坏了特征4。此外,这种情况很难处理。如果默认为红色,如果父节点为红色,那么根据红色就不能连续,所以我们需要旋转。但这只影响我们父子。
让我们先建立这个数据结构的框架。
模板类别K,类别V
类RBTree
{
公共:
typedef RBTreeNode K,V节点;
私人:
Node * _ pRoot=nullptr
};我们可以在这里插入元素。我们同意当前节点是cur,p是父节点,g是祖父节点,c是叔叔节点。插红黑树很重要。首先,我们之前的插页比较简单,就像二叉查找树一样。
布尔插入(常数对K,V数据)
{
//第一步是判断第一次插入
if (_pRoot==nullptr)
{
_pRoot=新节点(数据);
_pRoot- _col=黑色;
返回true
}
//步骤2搜索二叉树找到位置
Node * cur=_ pRoot
Node * parent=nullptr
而(cur!=nullptr)
{
if (cur- _kv.first data.first)
{
//去右边的树找
parent=cur
cur=cur-_ right;
}
else if (cur- _kv.first data.first)
{
//左树搜索
parent=cur
cur=cur-_ left;
}
其他
{
//我们这里不接受重复的值,你可以自己实现
返回false
}
}
//知道节点
Node* node=新节点(数据);
node- _col=红色;//记住不要相信构造函数,它可能不是你写的,也不会自动默认为0。
//插入元素
if(数据优先母公司- _kv优先)
{
parent-_ right=node;
}
其他
{
parent-_ left=node;
}
node-_ parent=parent;
cur=节点;
//.
}这里需要判断。想想我们的节点是红色的。如果我爸爸是黑人,我们可以直接插在这里,什么都不做。如果我父亲的节点是红色的,就会发生别的事情。
if (parent- _col==BLACK)
{
返回true
}我们开始处理父亲是红节点的情况,这里有几种情况。其中,我们发现后两种情况是一样的,只是做法不同。
大叔存在,是红的。大叔不存在也不存在,黑大叔不存在也不存在,黑大叔存在也红。我们先分析一下这个相对简单的情况。注意我们的是一个子树,当然也可能是一棵完整的树。
第一种情况,abcde是空树,其中cur是新节点,我们的父节点是红色的,所以不可能是有父节点的节点。所以这里一定有一个祖父节点。我们暂时假设我下面的树是一个完整的教训。这里有一个供讨论的问题集。
假设是一棵完整的树,但是这里根节点变红了,不符合要求。当我们插入它时,我们必须最终修改根节点的颜色,以确保它是黑色的。
布尔插入(常数对K,V数据)
{
//早期阶段
//插入操作
_pRoot- _col=黑色;
返回true
}具体案例2,ab不是空树,而且是红色的。那么cde就是以下任何一个xyzm,只要有黑节点。
然后cur由黑变红,也就是经过上面的步骤,爷爷把它赋给cur,重新判断。
随着我们逐渐向高层转变,后面的情况很多,但我们不会逃脱大叔存在和知名度的限制。这里就不讨论了。
案例3判断cur是P的左还是右,P是g的左还是右,这会影响案例1吗?不会,案例一方向不重要,因为它只变色不旋转。让我们以案例二为例。
while (parent parent- _col==RED)
{
node * Granger=parent-_ parent;
if (parent- _left==parent)
{
//判断大叔节点
节点*大叔=爷爷-_右;
if(大叔大叔- _col==RED)
{
//变色
parent-_ col=BLACK;
大叔-_ col=BALCK;
祖父- _col=红色;
cur=祖父;
parent=cur-_ parent;
}
Else //大叔不存在或者存在于黑
{
}
}
其他
{
Node*大叔=爷爷-_左;
//大叔存在并且是红色的
if(大叔大叔- _col==RED)
{
//变色
parent-_ col=BLACK;
大叔- _col=黑;
祖父- _col=红色;
cur=祖父;
parent=cur-_ parent;
}
其他
{
}
}
}大叔不存在或者存在又是黑的,这是我们红黑树的困境。需要设计成旋转的,但是单次旋转还是比较简单的。这里需要注意的是如何改变颜色。
叔叔不存在。很简单。cur是一个新节点。
第一种情况,abcde都是空树,cur是新加的,其中大叔不存在。你需要把父亲变成黑色,祖父变成红色,然后进行一次旋转。难得的是不是旋转,而是变色。确保这个子树的黑色节点不变,那么P会变黑,G会变红。我们不会让cur变黑,但是如果它变黑了,为什么不直接插入黑节点呢?
Cur是父的左边,parent是祖父的左边,右手cur是父的右边,parent是祖父的左右。
而且还有黑这种情况。cur不能是新的。cur本来是黑色的,但是从第一种情况开始变了。c是xyzm中的任意一个,D和E可能是空的,后者是红的。
当然,以上是我们讨论的最简单的情况。de也可以黑,加一层就行了。
这里就不解释了。反正涉及到旋转,只有左手和右手的区别。
node * Granger=parent-_ parent;
if (parent- _left==parent)
{
//判断大叔节点
节点*大叔=爷爷-_右;
if(大叔大叔- _col==RED)
{
//变色
parent-_ col=BLACK;
大叔-_ col=BALCK;
祖父- _col=红色;
cur=祖父;
parent=cur-_ parent;
}
Else //大叔不存在或者存在于黑
{
if (cur==parent- _left)
{
//g
//p
//c
//右撇子
RotateR(祖父);
//变色
parent-_ col=BLACK;
祖父- _col=红色;
}
其他
{
}
打破;
}
}
其他
{
Node*大叔=爷爷-_左;
//大叔存在并且是红色的
if(大叔大叔- _col==RED)
{
//变色
parent-_ col=BLACK;
大叔- _col=黑;
祖父- _col=红色;
cur=祖父;
parent=cur-_ parent;
}
其他
{
if (cur==parent- _right)
{
//g
//p
//c
//左撇子
RotateL(祖父);
//变色
parent-_ col=BLACK;
祖父- _col=红色;
}
其他
{
}
打破;
}
}大叔不存在或存在且黑。我们需要分析这种情况。我们会发现我们都在分析一条直线。和AVL树一样,我们也要考虑双旋的问题。在这里,我们将直接向您演示原理,而不是视具体情况而定。
node * Granger=parent-_ parent;
if (parent- _left==parent)
{
//判断大叔节点
节点*大叔=爷爷-_右;
if(大叔大叔- _col==RED)
{
//.
}
Else //大叔不存在或者存在于黑
{
if (cur==parent- _left)
{
}
其他
{
//g
//p
//c
//左撇子右撇子
RotateL(父母);
RotateR(祖父);
cur- _col=黑色;
祖父- _col=红色;
}
打破;
}
}
其他
{
Node*大叔=爷爷-_左;
//大叔存在并且是红色的
if(大叔大叔- _col==RED)
{
}
其他
{
if (cur==parent- _right)
{
}
其他
{
//g
//p
//c
//右撇子左撇子
RotateR(父母);
RotateL(祖父);
cur- _col=黑色;
祖父- _col=红色;
}
打破;
}
}旋转最后我们可以旋转写下来。AVL树种我们已经分析过了,这里就不解释了。
void RotateL(节点*父节点)
{
node * sub=parent-_ right;
node * subR=sub-_ left;
parent-_ right=subR;
if (subR)
subR-_ parent=parent;
node * prev=parent-_ parent;
sub-_ left=parent;
parent-_ parent=sub;
if (prev==nullptr)
{
_ pRoot=sub
_ pRoot-_ parent=nullptr;
}
其他
{
if (prev- _left==parent)
prev-_ left=sub;
其他
prev-_ right=sub;
sub-_ parent=prev;
}
}
空旋转器(节点*父节点)
{
node * subL=parent-_ left;
node * subLR=subL-_ right;
parent-_ left=subLR;
if (subLR)
subLR-_ parent=parent;
node * prev=parent-_ parent;
parent-_ parent=subL;
subL-_ right=parent;
//pParent可能不是根。
if (prev==nullptr)
{
_ pRoot=subL
_ pRoot-_ parent=nullptr;
返回;
}
if (prev- _left==parent)
prev-_ left=subL;
其他
prev-_ right=subL;
subL-_ parent=prev;
}验证红黑树我们需要验证自己写的红黑树是否正确。这里需要写一个函数来判断。
让我们先看看中序遍历,以确保我们是一个二叉查找树。
int main()
{
bit:RBTree int,int rb
for(int I=0;i 50我)
{
如果(i==4)
{
cout endl
}
rb。Insert(make_pair(i,I));
}
Rb . in order();
返回0;
}
我们需要测量无序度。
int main()
{
int N=10
srand(time(0));
bit:RBTree int,int rb
for(int I=0;I N;我)
{
int ret=rand();
rb。Insert(make_pair(ret,ret));
}
Rb . in order();
//Rb . level order();
返回0;
}
我们在这里思考,如果能知道它的最长路径和最短路径,是否能在某种程度上判断它是一棵红黑树?这种方法不太好,但在某种程度上是可能的。
int main()
{
int N=1024
bit:RBTree int,int rb
for(int I=0;I N;我)
{
rb。Insert(make_pair(i,I));
}
cout Rb . max height()endl;
cout Rb . min height()endl;
返回0;
}
我们发现最长路径不会超过最短路径的两倍。但不能确定红色节点是否连续。下面的方法是正确的。
公共:
bool IsValidRBTree()
{
Node * pRoot=_ pRoot
//空树也是红黑树
if (nullptr==pRoot)
返回true
//检查根节点是否满足条件。
如果(黑!=pRoot- _col)
{
“Cout”违反红黑树属性II:根节点必须是黑的“endl
返回false
}
//获取任意路径中黑色节点的数量
size _ t blackCount=0;
Node * pCur=pRoot
while (pCur)
{
if (BLACK==pCur- _col)
blackCount
pCur=pCur-_ left;
}
//检查是否满足红黑树的属性,用k记录路径中黑色节点的个数。
size _ t k=0;
return _IsValidRBTree(pRoot,k,black count);
}
私人:
bool _IsValidRBTree(Node* pRoot,size_t k,const size_t blackCount)
{
//去null后,判断K和black是否相等。
if (nullptr==pRoot)
{
如果(k!=黑计数)
{
“Cout”违例属性4:每个路径中的黑节点数必须相同“endl
返回false
}
返回true
}
//计算黑色节点的数量
if (BLACK==pRoot- _col)
k;
//检查当前节点及其父节点是否都是红色的。如果你遇到一个红色的节点,检查你的父亲
node * p parent=pRoot-_ parent;
if(p parent RED==p parent-_ col RED==pRoot-_ col)
{
Cout 违背自然3:没有红节点 endl 连在一起;
返回false
}
return _ IsValidRBTree(pRoot-_ left,k,blackCount)
_IsValidRBTree(pRoot- _right,k,black count);
}
int main()
{
int N=1024
bit:RBTree int,int rb
for(int I=0;I N;我)
{
rb。Insert(make_pair(i,I));
}
Cout 它平衡吗 rb。IsValidRBTree()endl;
返回0;
}
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。