目录
1.AVLTree概念
二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查
找元素相当于在一个链表中搜索元素,效率低下
于是有两位俄罗斯科学家提出了AVLTree的概念,来解决单支树的问题
在向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要
对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度
可以证明,如果它有n个结点,其高度可保持在 O(logN),搜索时间复杂度也是O(logN)
这里直接给出AVL树(高度平衡搜索二叉树)的定义
1.每个节点的左右子树高度之差的绝对值小于等于1
2.左右子树都为AVL树
2.AVLTree模拟实现
2.1 AVLTree节点
AVLTree实现的方法很多,这里采用的是平衡因子的实现方法
平衡因子是指每个节点右子树高度减去左子树高度的值
通过在每个节点设置一个balance factor,简称bf,就可以将代码简化,方便AVLTree之后的建树过程
template <class K,class V>
struct AVLTreeNode
{
AVLTreeNode<K, V>* _left;
AVLTreeNode<K, V>* _right;
//每个节点中还存一个指向父节点的指针
AVLTreeNode<K, V>* _parent;
pair<K, V> _kv;
int _bf; //balance factor
AVLTreeNode(const pair<K, V>& kv)
:_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_kv(kv)
,_bf(0)
{}
};
2.2 插入实现基本框架
AVL树,是一棵高度平衡二叉搜索树
先是二叉搜索树,再是平衡
所以说建一棵AVL树,实际上和建一棵二叉搜索树的步骤完全一样,只不过需要考虑平衡因子的问
题,一旦平衡因子不对劲,我们就要相应作出调整,从而实现高度平衡
那现在往一棵树插入新节点,我们肯定就需要更新平衡因子
这里就只会出现两种情况
第一,更新完以后,平衡因子没有出现问题,即bf绝对值小于等于1,那平衡结果就没有受影响,不需要处理
第二,更新完以后,平衡出现问题,即bf绝对值大于1,那就要作相应的旋转处理
比如我们往下面这棵树插入一个新的节点10,9的平衡因子就肯定需要调整,由原来的0变成1
相应的8的平衡因子也需要调整,由原来的1,变为2
但是我们可以注意到的是,插入新增节点,只会影响祖先的平衡因子,也就是一条路径上的点,而
不是同一条路径的节点平衡因子是不会受到影响的
像上面树中的2,插入新节点10,对于2来说,是没有任何影响的,平衡因子还是2,别人只是吃瓜
群众,别人诛九族也轮不到它
插入新增节点后,它的父亲节点的bf肯定是需要改变的,插入在右边,那bf就加1;插入在左边,
那bf就减1,像图中的父亲节点9,新增节点插入在它的右边,bf就加1,从原来的0变为1
那爷爷是否需要调整呢?或者更往上的祖先节点的bf是否需要调整呢?
这就需要分情况讨论,判断是否需要往上继续更新,大白话来说,就是看祖先节点的左右子树是否
有变化?变了,肯定就需要更新,没变,就不需要更新
像图中新插入节点10来说,对于8这个节点,右子树高度变了,由1变2,另外一边的左子树的高度
没变,还是0,那自然它的bf就需要改变
但实操上,并不需要这么麻烦,直接看父节点调整完后的bf即可
因为父节点就在爷爷节点的左子树或者右子树之中
假如父节点调整完后的bf为0,说明之前一边高一边低,插入的新增节点刚好填上矮的那边,高度
没有变,那爷爷节点的bf肯定也不用变化,因为子树高度就没有变
那假如父节点调整完后的bf为1或者-1,说明插入前左右高度相等,现在有一边高,子树的高度改
变了,那对应爷爷节点的bf肯定也要变化
而最复杂的情况就是bf为2或者-2,此时子树就需要旋转调整,来保持仍然为一棵AVL树
至此,我们已经可以搭出插入的基本框架
bool Insert(const pair<K, V>& kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
return true;
}
//是一棵搜索树,插入前要找到对应的正确位置插入
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_kv.first < kv.first)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_kv.first > kv.first)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
//建新节点,并完成与父节点的链接
cur = new Node(kv);
if (parent->_kv.first > kv.first)
{
parent->_left = cur;
}
else
{
parent->_right = cur;
}
cur->_parent = parent;
// 更新平衡因子
while (parent)
{
//只要插入新节点,其父节点的bf肯定会改变
//在右边就加1,在左边就减1
if (cur == parent->_right)
{
parent->_bf++;
}
else
{
parent->_bf--;
}
//判断祖先节点的bf是否还需要改变
if (parent->_bf == 1 || parent->_bf == -1)
{
// 继续更新
parent = parent->_parent;
cur = cur->_parent;
}
else if (parent->_bf == 0)
{
break;
}
else if (parent->_bf == 2 || parent->_bf == -2)
{
// 需要旋转处理 -- 1、让这颗子树平衡 2、降低这颗子树的高度
if (parent->_bf == 2 && cur->_bf == 1)
{
RotateL(parent);
}
else if (parent->_bf == -2 && cur->_bf == -1)
{
RotateR(parent);
}
else if (parent->_bf == -2 && cur->_bf == 1)
{
RotateLR(parent);
}
else if (parent->_bf == 2 && cur->_bf == -1)
{
RotateRL(parent);
}
else
{
//能来到这里,说明树之前就出了问题,本身就不是一棵AVL树
assert(false);
}
}
return true;
}
现在我们差的就是旋转处理的实现,目的就是
让这棵子树重新平衡,同时降低这棵子树的高度
在父节点的bf == 2或者bf == -2的前提下,我们可以单独将这棵子树抽离出来分析,可以分为两种
情况
bf == 2意味着新插入的节点在右子树
bf == -2则意味着新插入的节点在左子树
但是情况还没有这么简单,新增的节点还可能插入在右子树的右边,或者是左边;在左子树的左边
或者是右边,这四种情况的旋转方式是不同的,具体原因,在后面分析的时候,我们就会有更深的
体会
2.3 左单旋
旋转处理目的是
让这棵子树重新平衡,同时降低这棵子树的高度
现在我们抽象出来这样一个右高左低的模型,调整为怎么样的模型,是降低这棵子树的高度呢?
答案是将它折下来,将中间的节点提上来,在保持它仍然是一棵搜索二叉树情况下,形成一个倒v
字
有了前面的铺垫,我们就可以看具体左单旋的场景(PS:注意此时的30和60这两个数字并不是绝对
意义上的60和30,只是说明60这个节点的值要大于30这个节点的值)
若把30称作parent节点,60称作为subR节点,很容易发现,当往c这棵子树新插入节点的时候,
parent对应的bf就由原来的1变为2,subR(cur)就由原来的0变为1,符合我们之前所定义的需要右
旋的情况
那具体如何旋转呢?就是按照倒v模型
1.b变成30的右边
2.30变成60的左边
60变成整棵树的根
我们可以举一些具体的例子,毕竟高度为h的树a,b,c毕竟是抽象的
当h == 0时,a,b,c树都为空,新增节点直接插入到60上,按我们之前分析的方式进行旋转
完全可以达到旋转的目的
当h == 1时,a,b,c树都为一个节点,新增节点直接插入到c树的右边或者左边都行,都会导致
parent的bf为2,cur的bf为1,按我们之前分析的方式进行旋转,也完全可以达到旋转的目的
当h == 2时,情况就会复杂很多,毕竟三个节点,能够组成的情况本身就有3种
不过a,b,c子树并不是任意取的,有一定的限制,即c树必须为x的形状,假如不是x的形状,那往c
树这边插入新节点,是不会导致parent的bf变为2,cur的bf变为1这个右旋前提的
最简单的比如,假如c是y的形状,那新增一个右节点,甚至不需要旋转,直接插入即可
如果新增一个左节点,此时就要左旋或者左右双旋
而a,b子树可以为x,y,z中的任意一种,总共3*3*1 *4 (4个插入位置)= 36种情况
这里简单列举全部都为x形状的情况
可以看到倒V形仍然适用
有了思路,代码实现起来就很轻松,按部就班链接节点即可
不过,还有几点需要注意
第一,链接的同时,记得节点的_parent也要调整
第二,subRL可能为空,所以在链接其父亲的时候,要先判别其是否为空,防止空指针解引用
第三,调整时未必是整棵树调整,所以还需要考虑链接会大树的问题
第四,平衡因子记得也要调整
//左旋
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
//b变成30的右
parent->_right = subRL;
//父节点也需要调整,但subRL可能为空
if (subRL)
subRL->_parent = parent;
//调整时未必是整棵树的调整,所以还需要考虑parent的链接问题,因此需要先记录ppNode
Node* ppNode = parent->_parent;
subR->_left = parent;
parent->_parent = subR;
if (ppNode == nullptr)
{
_root = subR;
_root->_parent = nullptr;
}
else
{
//在调整爷爷节点指向的时候,还需要考虑原来parent是爷爷的左还是右
//subR重新链接回爷爷的左或者右
if (ppNode->_right == parent)
{
ppNode->_right = subR;
}
else
{
ppNode->_left = subR;
}
subR->_parent = ppNode;
}
//调整平衡因子
parent->_bf = subR->_bf = 0;
}
2.4 右单旋
右旋和左旋类似,不过采取的是另外一种左高右低的模型
不过虽然模型不同,但旋转调整的目的依旧不变
让这棵子树重新平衡,同时降低这棵子树的高度
因此右旋的结果,依旧和左旋保持一致,将中间节点提上去,保证它依旧是搜索二叉树下,形成一
个倒V模型
右单旋的具体场景也和左单旋非常类似
若把60称作parent节点,30称作为subL节点,很容易发现,当往a这棵子树新插入节点的时候,
parent对应的bf就由原来的-1变为-2,subL(cur)就由原来的0变为-1,符合我们之前所定义的需要右
旋的情况
那具体如何旋转呢?就是按照倒v模型
1.b变成60的左边
2.60变成30的右边
30变成整棵树的根
同样的,左旋需要注意的点,右旋也同样要注意
第一,链接的同时,记得节点的_parent也要调整
第二,subRL可能为空,所以在链接其父亲的时候,要先判别其是否为空,防止空指针解引用
第三,调整时未必是整棵树调整,所以还需要考虑链接会大树的问题
第四,平衡因子记得也要调整
//右旋
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
//b变成60的左
parent->_left = subLR;
//父节点也需要调整,但subRL可能为空
if (subLR)
subLR->_parent = parent;
//调整时未必是整棵树的调整,所以还需要考虑parent的链接问题,因此需要先记录ppNode
Node* ppNode = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
if (ppNode == nullptr)
{
_root = subL;
_root->_parent = nullptr;
}
else
{
//在调整爷爷节点指向的时候,还需要考虑原来parent是爷爷的左还是右
//subL重新链接回爷爷的左或者右
if (ppNode->_right == parent)
{
ppNode->_right = subL;
}
else
{
ppNode->_left = subL;
}
subL->_parent = ppNode;
}
//调整平衡因子
parent->_bf = subL->_bf = 0;
}
2.5 LR双旋
之所以会出现双旋概念,就是因为单纯的单旋无法解决某类模型
假如对这类模型进行左旋,充其量也仅仅是做一个镜像对称,没有解决任何问题
但上面得到的模型,显然没有满足我们旋转的目的
所以我们才需要采用双旋来解决
先对下面的两个节点来个左旋,变成我们最熟悉的模型,再对其进行右旋
即可变成我们想要的倒V模型,让这棵子树重新平衡,同时降低这棵子树的高度
具体的模型如下
若把60称作parent节点,30称作为subL节点,很容易发现,当往b,c这两棵子树任意位置新插入节
点的时候,parent对应的bf就由原来的-1变为-2,subL(cur)就由原来的0变为1,符合我们之前所定
义的需要LR双旋的情况
由于前面已经实现过左单旋,和右单旋,所以双旋直接赋用即可
先对30来个左旋
再对90来个右旋
我们遮住其中的变化过程,只看首尾变化的话,也可以进一步理解LR双旋具体如何操作
subLR当根,然后把它的左子树分给subL,将它的右子树分给parent,保证了它仍然是一棵搜索二叉树,并达到高度平衡
不过虽然完成了调整,但双旋真正的难点在于平衡因子的调整,单纯的单旋,平衡因子都会被调整
为0,但是实际最后的平衡因子,按照图片也知道并不是0
往b插入新节点,和往c插入新节点,最后平衡因子的变化是不同的;h==0时,插入新节点,平衡
因子变化也是不同的,需要分类讨论
当往b插入新节点,b树的高度就为h,则subL的bf就为0,与之相对,parent的bf就要变为1
subLR的bf始终为0
当往c插入新节点,c树的高度就为h,则subL的bf就为-1,与之相对,parent的bf就要变为0
subLR的bf始终为0
当h == 0时,不需要调整,三个节点的bf都为0
//左右双旋
void RotateLR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
//记录subLR初始的bf值,后续平衡因子调整需要使用
int bf = subLR->_bf;
//先对30来个左旋,再对90来个右旋
//RotateL(subL);
RotateL(parent->_left);
RotateR(parent);
//平衡因子调整有三种情况,一是b新增,二是c新增,三是h == 0的情况
//c新增
if (bf == 1)
{
parent->_bf = 0;
subLR->_bf = 0;
subL->_bf = -1;
}
//b新增
else if (bf == -1)
{
parent->_bf = 1;
subLR->_bf = 0;
subL->_bf = 0;
}
else if (bf == 0)
{
parent->_bf = 0;
subLR->_bf = 0;
subL->_bf = 0;
}
else
{
assert(false);
}
}
2.6 RL双旋
RL双旋和LR双旋就是类似的操作了
它的提出也是因为单选无法解决这类模型,而且的的确确存在这种情况
对它直接进行右旋,只会得到一个镜像模型,当然可以先右旋,再LR双旋,理论上也是可以的
类似的,先对下面的两个节点来个右旋,变成我们最熟悉的模型,再对其进行左旋
即可变成我们想要的倒V模型,让这棵子树重新平衡,同时降低这棵子树的高度
具体的模型如下
类似操作,先对90左旋
再对30右旋
我们遮住其中的变化过程,只看首尾变化的话,也可以进一步理解RL双旋具体如何操作
subRL当根,然后把它的左子树分给parent,将它的右子树分给subR,保证了它仍然是一棵搜索二叉树,并达到高度平衡
同样的,RL双旋的平衡因子都会被调整,依旧要对它进行分类讨论调整
当往b插入新节点,b树的高度就为h,则parent的bf就为0,与之相对,subR的bf就要变为1
subLR的bf始终为0
当往c插入新节点,c树的高度就为h,则subR的bf就为0,与之相对,parent的bf就要变为-1
subLR的bf始终为0
当h == 0时,不需要调整,三个节点的bf都为0
//右左双旋
void RotateRL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
//记录subLR初始的bf值,后续平衡因子调整需要使用
int bf = subRL->_bf;
//先对30来个左旋,再对90来个右旋
RotateR(parent->_right);
RotateL(parent);
//平衡因子调整有三种情况,一是b新增,二是c新增,三是h == 0的情况
//c新增
if (bf == 1)
{
parent->_bf = -1;
subRL->_bf = 0;
subR->_bf = 0;
}
//b新增
else if (bf == -1)
{
parent->_bf = 0;
subRL->_bf = 0;
subR->_bf = 1;
}
else if (bf == 0)
{
parent->_bf = 0;
subRL->_bf = 0;
subR->_bf = 0;
}
else
{
assert(false);
}
}
2.7 AVLTree树验证
验证一棵二叉树为AVL树,有三个方面需要验证
1.高度差是否正确
2.每个节点的平衡因子是否正确
3.是否为一棵二叉搜索树,中序遍历是否有序
template <class K,class V>
struct AVLTreeNode
{
AVLTreeNode<K, V>* _left;
AVLTreeNode<K, V>* _right;
//每个节点中还存一个指向父节点的指针
AVLTreeNode<K, V>* _parent;
pair<K, V> _kv;
int _bf; //balance factor
AVLTreeNode(const pair<K, V>& kv)
:_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_kv(kv)
,_bf(0)
{}
};
template <class K, class V>
class AVLTree
{
typedef AVLTreeNode<K, V> Node;
public:
//插入
bool Insert(const pair<K, V>& kv)
{
//假如一开始没有节点,则直接建节点作为根
if (_root == nullptr)
{
_root = new Node(kv);
return true;
}
//找出对应插入的新位置,与二叉树的插入操作相同
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
//假如插入的值,比当前节点的值大,则往右移动
if (cur->_kv.first < kv.first)
{
parent = cur;
cur = cur->_right;
}
//假如插入的值,比当前节点的值小,则往左移动
else if (cur->_kv.first > kv.first)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
//找到对应插入新节点的位置,建节点
cur = new Node(kv);
//将节点链接到AVLTree上
if (parent->_kv.first > cur->_kv.first)
{
parent->_left = cur;
}
else
{
parent->_right = cur;
}
cur->_parent = parent;
//更新因子
while (parent)
{
//插入新增节点,会影响祖先的平衡因子
//首先父亲节点的平衡因子肯定会改变
if (cur == parent->_right)
{
parent->_bf++;
}
else
{
parent->_bf--;
}
//考虑是否要向上更新的问题
//而是否要向上更新,按照AVLTree的规则就是看爷爷节点的bf是否被破坏
//假如没有变了,则需要更新;没变,则不需要更新
if (parent->_bf == 1 || parent->_bf == -1)
{
//继续向上更新
parent = parent->_parent;
cur = cur->_parent;
}
else if (parent->_bf == 0)
{
break;
}
else if(parent->_bf == 2 || parent->_bf == -2)
{
// 需要旋转处理 -- 1、让这颗子树平衡 2、降低这颗子树的高度
if (parent->_bf == 2 && cur->_bf == 1)
{
RotateL(parent);
}
else if (parent->_bf == -2 && cur->_bf == -1)
{
RotateR(parent);
}
else if (parent->_bf == -2 && cur->_bf == 1)
{
RotateLR(parent);
}
else if (parent->_bf == 2 && cur->_bf == -1)
{
RotateRL(parent);
}
else
{
assert(false);
}
break;
}
else
{
assert(false);
}
}
return true;
}
//中序遍历
void Inorder()
{
_Inorder(_root);
cout << endl;
}
//验证AVLTree是否建成功
bool IsBalance()
{
return _IsBalance(_root);
}
//求树的高度
int Height()
{
return _Height(_root);
}
private:
int _Height(Node* root)
{
if (root == NULL)
return 0;
int leftH = _Height(root->_left);
int rightH = _Height(root->_right);
return leftH > rightH ? leftH + 1 : rightH + 1;
}
//判断右子树和左子树高度差绝对值是否小于1
bool _IsBalance(Node* root)
{
if (root == nullptr)
return true;
int leftH = _Height(root->_left);
int rightH = _Height(root->_right);
//平衡因子是否更新正确
if (rightH - leftH != root->_bf)
{
cout << root->_kv.first << "节点平衡因子异常" << endl;
return false;
}
//从下往上,每一棵子树都是一棵平衡二叉树,则就是一棵平衡二叉树
return abs(rightH - leftH) < 2
&& _IsBalance(root->_left)
&& _IsBalance(root->_right);
}
//左旋
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
//b变成30的右
parent->_right = subRL;
//父节点也需要调整,但subRL可能为空
if (subRL)
subRL->_parent = parent;
//调整时未必是整棵树的调整,所以还需要考虑parent的链接问题,因此需要先记录ppNode
Node* ppNode = parent->_parent;
subR->_left = parent;
parent->_parent = subR;
if (ppNode == nullptr)
{
_root = subR;
_root->_parent = nullptr;
}
else
{
//在调整爷爷节点指向的时候,还需要考虑原来parent是爷爷的左还是右
//subR重新链接回爷爷的左或者右
if (ppNode->_right == parent)
{
ppNode->_right = subR;
}
else
{
ppNode->_left = subR;
}
subR->_parent = ppNode;
}
//调整平衡因子
parent->_bf = subR->_bf = 0;
}
//右旋
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
//b变成60的左
parent->_left = subLR;
//父节点也需要调整,但subRL可能为空
if (subLR)
subLR->_parent = parent;
//调整时未必是整棵树的调整,所以还需要考虑parent的链接问题,因此需要先记录ppNode
Node* ppNode = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
if (ppNode == nullptr)
{
_root = subL;
_root->_parent = nullptr;
}
else
{
//在调整爷爷节点指向的时候,还需要考虑原来parent是爷爷的左还是右
//subL重新链接回爷爷的左或者右
if (ppNode->_right == parent)
{
ppNode->_right = subL;
}
else
{
ppNode->_left = subL;
}
subL->_parent = ppNode;
}
//调整平衡因子
parent->_bf = subL->_bf = 0;
}
//左右双旋
void RotateLR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
//记录subLR初始的bf值,后续平衡因子调整需要使用
int bf = subLR->_bf;
//先对30来个左旋,再对90来个右旋
//RotateL(subL);
RotateL(parent->_left);
RotateR(parent);
//平衡因子调整有三种情况,一是b新增,二是c新增,三是h == 0的情况
//c新增
if (bf == 1)
{
parent->_bf = 0;
subLR->_bf = 0;
subL->_bf = -1;
}
//b新增
else if (bf == -1)
{
parent->_bf = 1;
subLR->_bf = 0;
subL->_bf = 0;
}
else if (bf == 0)
{
parent->_bf = 0;
subLR->_bf = 0;
subL->_bf = 0;
}
else
{
assert(false);
}
}
//右左双旋
void RotateRL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
//记录subLR初始的bf值,后续平衡因子调整需要使用
int bf = subRL->_bf;
//先对30来个左旋,再对90来个右旋
RotateR(parent->_right);
RotateL(parent);
//平衡因子调整有三种情况,一是b新增,二是c新增,三是h == 0的情况
//c新增
if (bf == 1)
{
parent->_bf = -1;
subRL->_bf = 0;
subR->_bf = 0;
}
//b新增
else if (bf == -1)
{
parent->_bf = 0;
subRL->_bf = 0;
subR->_bf = 1;
}
else if (bf == 0)
{
parent->_bf = 0;
subRL->_bf = 0;
subR->_bf = 0;
}
else
{
assert(false);
}
}
void _Inorder(Node* root)
{
if (root == nullptr)
{
return;
}
_Inorder(root->_left);
cout << root->_kv.first << " ";
_Inorder(root->_right);
}
private:
Node* _root = nullptr;
};
分别用两组测试代码进行测试
第一组为一固定的具体值,首先通过它来看代码整体是否有误
void Test_AVLTree1()
{
//int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
AVLTree<int, int> t1;
for (auto e : a)
{
/* if (e == 14)
{
int x = 0;
}*/
t1.Insert(make_pair(e, e));
cout << e << "插入:" << t1.IsBalance() << endl;
}
t1.Inorder();
cout << t1.IsBalance() << endl;
}
对应结果
第二组为多组随机值,如果能够全部每次通过,则代码整体没有问题
void Test_AVLTree2()
{
srand(time(0));
const size_t N = 100000;
AVLTree<int, int> t;
for (size_t i = 0; i < N; ++i)
{
size_t x = rand() + i;
t.Insert(make_pair(x, x));
//cout << t.IsBalance() << endl;
}
//t.Inorder();
cout << t.IsBalance() << endl;
//cout << t.Height() << endl;
}
对应结果,由于数据过大,所以这里没有采取中序遍历全部打印