目录

​一,平衡无序二叉树​

​二,平衡排序二叉树(Self-balancing binary search tree)​

​三,平衡二叉树(AVL树)​

​四,查询​

​五,插入节点​

​1,插入一个新节点,变成二叉排序树​

​2,调整深度使得它变成平衡二叉树​

​(1)不平衡点的分布​

​(2)最近不平衡点​

​(3)调整深度​

​3,完整插入代码(版本一)​

​六,删除节点​

​1,插入代码重构​

​2,完整插入代码(版本二)​

​3,插入调整的另外一个思路——覆盖旋

​4,完整插入代码(版本三)​

​5,删除节点​

​七,OJ实战​

​1,平衡无序二叉树​

​力扣 110. 平衡二叉树​

​2,平衡排序二叉树​

​力扣 108. 将有序数组换为二叉搜索树​

​力扣 109. 有序链表换二叉搜索树​



一,平衡无序二叉树

其实并没有“平衡无序二叉树”这个词,但是“平衡二叉树”这个词的概念一直有两个流派,为了区分,我发明了这个词。

如果一个二叉树满足任意节点的俩子树的高度差都小于等于1,那么它就是平衡无序二叉树。

完全二叉树就是一种平衡无序二叉树。

PS:如若任意节点的俩子树的高度差都为0,那它就只能是满二叉树了。


二,平衡排序二叉树(Self-balancing binary search tree)

如果一个二叉树,既是平衡无序二叉树,又是二叉搜索树(二叉排序树),则称之为平衡排序二叉树。


三,平衡二叉树(AVL树)

关于平衡二叉树,有很多人认为指的是平衡无序二叉树,也有很多人认为是平衡排序二叉树。

从字面意思来看,是平衡无序二叉树,但是从实际应用场景来看,一般指的都是平衡排序二叉树。

我支持平衡二叉树就是平衡排序二叉树的简称这一观点,毕竟都是从英文翻译来的,名称不准确。

平衡二叉树也叫AVL树,取自人名。


四,查询

平衡二叉树作为一棵二叉搜索树,查询的过程和普通二叉搜索树完全相同。

struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
};

TreeNode* find(TreeNode* root, int val)
{
if(!root)return NULL;
if(root->val==val)return root;
if(root->val>val)return find(root->left,val);
return find(root->right,val);
}


五,插入节点

插入节点分为两步,先插入一个新节点,变成二叉排序树,如果它不是平衡二叉树,那么就进行第二步,调整深度使得它变成平衡二叉树

神奇的是,​这两步是解耦的,第一步采取什么样的方案不影响第二步的策略,只要第一步的插入操作是正规插入。

这里的正规插入指的是,在某个节点A下,把new节点插入到A和A的某个子树(子树可能为空)中间,除此之外不对原树进行任何修改。

1,插入一个新节点,变成二叉排序树

这一步可以有很多方案,只要满足上面说的正规插入即可。

例如,可以和二叉排序树的插入节点的方法一样,找到某个位置插入new节点之后,new节点是叶子节点。

如果采取这种方案的话,插入代码就和二叉搜索树里面的完全一样。

void InsertNode(TreeNode* &root, TreeNode* node)
{
if(!root)root=node;
else if(root->val>node->val)InsertNode(root->left,node);
else InsertNode(root->right,node);
}

2,调整深度使得它变成平衡二叉树

(1)不平衡点的分布

插入new节点之后,如果对于某个节点A,以A为根的二叉树不是平衡二叉树,我们称之为不平衡点。

从根节点到new节点的最短路径中,一定包含了所有的不平衡点。

在这条路径上,不平衡点的分布又有什么规律呢?

举个栗子:

平衡二叉树(AVL树)_平衡二叉树

可以说有规律,和节点两边的高度差有关,如果高度差是1,然后刚好在高度更高的那边插入了叶子节点并且使得高度加1,那么就会产生一个不平衡点。

PS:有可能有若干个不平衡点,但是这些点中不包含root节点。

PPS:无论整棵树的高度有没有加1,都有可能有若干个不平衡点,但是这些点中不包含root节点。

(2)最近不平衡点

在所有不平衡点中,找到离new节点最近的那个,我们称之为最近不平衡点N。

只要采取正规调整法,使得以N为根的树变成平衡二叉树,其他的不平衡点都会自动消失,整颗树都会变成平衡二叉树

这里的正规调整指的是,让new节点所在的子树的深度减1,这样以N为根的树就变成平衡二叉树了,此时,以N为根的树的深度也减1了,而所有不平衡点又都是N的祖先,于是所有的不平衡点都消失了。

寻找最近不平衡点的代码:

//ans是出参,返回值是深度
int GetNearestNoBalanceNode(TreeNode* root, TreeNode* &ans)
{
if(!root)return 0;
int d1=GetNearestNoBalanceNode(root->left,ans);
int d2=GetNearestNoBalanceNode(root->right,ans);
if(d1<0 || d2<0)return -1;
if(d1-d2>1 || d2-d1>1){
ans=root;
return -1;
}
ans = NULL;
return ((d1<d2)?d2:d1)+1;
}

最终结果为空指针就代表没有不平衡点

(3)调整深度

new节点一定不是N节点(最近不平衡点),也不是N节点的孩子,即两者的深度差至少是二

N节点的孩子的孩子一共有4个,LL,RR,LR,RL,这四个里面有一个是new节点的祖先(包括就是new节点的情况)

首先确定,到底哪一个是new节点的祖先:

void CC(TreeNode* root, TreeNode* node, TreeNode* &c1, TreeNode* &c2)
{
if(root->val>node->val)c1=root->left;
else c1=root->right;
if(c1->val>node->val)c2=c1->left;
else c2=c1->right;
}

root是最近不平衡点,c1是root的孩子,c2是c1的孩子,c2就是new节点的祖先。

因为new节点的存在性,所以这里不需要指针判空。

然后通过旋,让c1和c2的位置互换:

void RotateLL(TreeNode* root, TreeNode* c1, TreeNode* c2)
{
root->left=c2,c1->left=c1->right,c1->right=root->right,root->right=c1;
int val=root->val;
root->val=c1->val,c1->val=val;
}
void RotateRR(TreeNode* root, TreeNode* c1, TreeNode* c2)
{
root->right=c2,c1->right=c1->left,c1->left=root->left,root->left=c1;
int val=root->val;
root->val=c1->val,c1->val=val;
}
void RotateLR(TreeNode* root, TreeNode* c1, TreeNode* c2)
{
root->left=c2,c1->right=c2->left,c2->left=c1;
RotateLL(root,c2,c1);
}
void RotateRL(TreeNode* root, TreeNode* c1, TreeNode* c2)
{
root->right=c2,c1->left=c2->right,c2->right=c1;
RotateRR(root,c2,c1);
}
void Rotate(TreeNode* root, TreeNode* c1, TreeNode* c2)
{
if(root->left==c1){
if(c1->left==c2)RotateLL(root,c1,c2);
else RotateLR(root,c1,c2);
} else{
if(c1->left==c2)RotateRL(root,c1,c2);
else RotateRR(root,c1,c2);
}
}

最后,综合起来就得到了完整了深度调整方案:

void Adjust(TreeNode* root, TreeNode* node)
{
TreeNode*p,*c1,*c2;
GetNearestNoBalanceNode(root,p);
if(p==NULL)return;
CC(p,node,c1,c2);
Rotate(p,c1,c2);
}

下文的力扣 108、109这2个题目中直接用我这个插入接口做的,所以就不需要再额外做测试了。

3,完整插入代码(版本一)

void InsertNode(TreeNode* &root, TreeNode* node)
{
if(!root)root=node;
else if(root->val>node->val)InsertNode(root->left,node);
else InsertNode(root->right,node);
}

//ans是出参,返回值是深度
int GetNearestNoBalanceNode(TreeNode* root, TreeNode* &ans)
{
if(!root)return 0;
int d1=GetNearestNoBalanceNode(root->left,ans);
int d2=GetNearestNoBalanceNode(root->right,ans);
if(d1<0 || d2<0)return -1;
if(d1-d2>1 || d2-d1>1){
ans=root;
return -1;
}
ans = NULL;
return ((d1<d2)?d2:d1)+1;
}

void CC(TreeNode* root, TreeNode* node, TreeNode* &c1, TreeNode* &c2)
{
if(root->val>node->val)c1=root->left;
else c1=root->right;
if(c1->val>node->val)c2=c1->left;
else c2=c1->right;
}

void RotateLL(TreeNode* root, TreeNode* c1, TreeNode* c2)
{
root->left=c2,c1->left=c1->right,c1->right=root->right,root->right=c1;
int val=root->val;
root->val=c1->val,c1->val=val;
}
void RotateRR(TreeNode* root, TreeNode* c1, TreeNode* c2)
{
root->right=c2,c1->right=c1->left,c1->left=root->left,root->left=c1;
int val=root->val;
root->val=c1->val,c1->val=val;
}
void RotateLR(TreeNode* root, TreeNode* c1, TreeNode* c2)
{
root->left=c2,c1->right=c2->left,c2->left=c1;
RotateLL(root,c2,c1);
}
void RotateRL(TreeNode* root, TreeNode* c1, TreeNode* c2)
{
root->right=c2,c1->left=c2->right,c2->right=c1;
RotateRR(root,c2,c1);
}
void Rotate(TreeNode* root, TreeNode* c1, TreeNode* c2)
{
if(root->left==c1){
if(c1->left==c2)RotateLL(root,c1,c2);
else RotateLR(root,c1,c2);
} else{
if(c1->left==c2)RotateRL(root,c1,c2);
else RotateRR(root,c1,c2);
}
}

void Adjust(TreeNode* root, TreeNode* node)
{
TreeNode*p,*c1,*c2;
GetNearestNoBalanceNode(root,p);
if(p==NULL)return;
CC(p,node,c1,c2);
Rotate(p,c1,c2);
}

void InsertAndAdjust(TreeNode* &root, int val)
{
TreeNode* p = find(root,val);
if(p)return;
p= new TreeNode();
p->val=val;
p->left=NULL, p->right=NULL;
InsertNode(root,p);
Adjust(root,p);
}

六,删除节点

删除节点的思路,比插入节点的思路更复杂。

1,插入代码重构

在写插入节点的代码时,我就已经注意到,CC函数和Rotate函数的逻辑基本是重复的。

CC函数是new节点和最近不平衡点的位置关系,结果是LL,LR,RL,RR四种,Rotate函数是根据CC函数的结果来确认调用哪个函数

其中CC函数的实现,有2种思路,一种是根据new节点的val值来计算,一种是根据两边的深度来计算,虽然结果是一样的,但是其实深度才是本质

我把LL和LR两种情况的图示画出来了:

平衡二叉树(AVL树)_平衡二叉树_02

ABC是节点,1234是四颗树,其中,A就是最近不平衡点,3或者4就是插入new节点的位置,插入之后3或4的长度减1的长度等于二。

经过调整后,1的长度加一,3和4的长度减一,2的长度不变,于是就变成了平衡二叉树。

其他的,比如LL中,1和2都变成了A的子树,那么A还是平衡二叉树吗?是的,这个是可以推导出来的,其他的也是。

再次重申,3和4都有可能是空树,甚至都是空树,new节点可能就是C,这不影响我们的计算,甚至自然语言的描述都是一致的。

至此,我们就得到了关于插入节点之后如何调整的更有力的表述:

先找到不平衡点A,计算两颗子树的深度,深度差一定刚好是2,通过简单的调整,两颗子树的深度分别加1和减1,调整之后两边深度相等

基于此,我们可以把插入的代码进行微重构:

首先,删掉CC函数,并修改Rotate函数:

int maxDepth(TreeNode* root) {
if (!root)return 0;
return max(maxDepth(root->left), maxDepth(root->right)) + 1;
}
void Rotate(TreeNode* root)
{
if (maxDepth(root->left) > maxDepth(root->right) + 1) {
if (maxDepth(root->left->left) > maxDepth(root->left->right))RotateLL(root, root->left, root->left->left);
else RotateLR(root, root->left, root->left->right);
} else if (maxDepth(root->right) > maxDepth(root->left) + 1) {
if (maxDepth(root->right->right) > maxDepth(root->right->left))RotateRR(root, root->right, root->right->right);
else RotateRL(root, root->right, root->right->left);
}
}

void Adjust(TreeNode* root, TreeNode* node)
{
TreeNode* p, * c1, * c2;
GetNearestNoBalanceNode(root, p);
if (p == NULL)return;
Rotate(p);
}

其次,我们发现各个旋函数其实并不需要传参,可进一步重构。

2,完整插入代码(版本二)

#include <iostream>
using namespace std;

struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
};

void InsertNode(TreeNode*& root, TreeNode* node)
{
if (!root)root = node;
else if (root->val > node->val)InsertNode(root->left, node);
else InsertNode(root->right, node);
}

//ans是出参,返回值是深度
int GetNearestNoBalanceNode(TreeNode* root, TreeNode*& ans)
{
if (!root)return 0;
int d1 = GetNearestNoBalanceNode(root->left, ans);
int d2 = GetNearestNoBalanceNode(root->right, ans);
if (d1 < 0 || d2 < 0)return -1;
if (d1 - d2 > 1 || d2 - d1 > 1) {
ans = root;
return -1;
}
ans = NULL;
return ((d1 < d2) ? d2 : d1) + 1;
}

int maxDepth(TreeNode* root) {
if (!root)return 0;
return max(maxDepth(root->left), maxDepth(root->right)) + 1;
}

void RotateLL(TreeNode* root)
{
TreeNode* c1 = root->left, * c2 = c1->left;
root->left = c2, c1->left = c1->right, c1->right = root->right, root->right = c1;
int val = root->val;
root->val = c1->val, c1->val = val;
}
void RotateRR(TreeNode* root)
{
TreeNode* c1 = root->right, * c2 = c1->right;
root->right = c2, c1->right = c1->left, c1->left = root->left, root->left = c1;
int val = root->val;
root->val = c1->val, c1->val = val;
}
void RotateLR(TreeNode* root)
{
TreeNode* c1 = root->left, * c2 = c1->right;
root->left = c2, c1->right = c2->left, c2->left = c1;
RotateLL(root);
}
void RotateRL(TreeNode* root)
{
TreeNode* c1 = root->right, * c2 = c1->left;
root->right = c2, c1->left = c2->right, c2->right = c1;
RotateRR(root);
}

void Rotate(TreeNode* root)
{
if (maxDepth(root->left) > maxDepth(root->right) + 1) {
if (maxDepth(root->left->left) > maxDepth(root->left->right))RotateLL(root);
else RotateLR(root);
} else if (maxDepth(root->right) > maxDepth(root->left) + 1) {
if (maxDepth(root->right->right) > maxDepth(root->right->left))RotateRR(root);
else RotateRL(root);
}
}

void Adjust(TreeNode* root, TreeNode* node)
{
TreeNode* p;
GetNearestNoBalanceNode(root, p);
if (p == NULL)return;
Rotate(p);
}

void InsertAndAdjust(TreeNode*& root, int val)
{
TreeNode* p = find(root,val);
if(p)return;
p= new TreeNode();
p->val = val;
p->left=NULL, p->right=NULL;
InsertNode(root, p);
Adjust(root, p);
}

int main()
{
TreeNode* root = NULL;
InsertAndAdjust(root, -10);
InsertAndAdjust(root, -3);
InsertAndAdjust(root, 0);
InsertAndAdjust(root, 5);
InsertAndAdjust(root, 9);
return 0;
}

当然,这个版本也还有很多可以改进的地方,比如maxDepth函数调用多次,影响效率,这个很容易优化掉,不过并不是我们的主逻辑,略过。

这里得到的Rotate函数有个巨大的价值就在于,它可以直接通用于插入场景和删除场景

3,插入调整的另外一个思路——覆盖旋转

前面的思路是,先找到最近不平衡点,对这个节点调用Rotate函数即可平衡,而且此时整棵树都会变成平衡二叉树,不再有不平衡点。

插入和删除最大的区别在于,删除一个节点并消除最近不平衡点之后,可能还有很多很多不平衡点等待消除

当然,共同点就是,所有的不平衡点依然是删除节点的祖先。

如果把插入节点或者删除节点的所有祖先全部调用一次Rotate函数,那么整颗树就会变成平衡二叉树,我们也会得到更加通用的代码。

要实现这个覆盖,有两种方式,一种是从插入节点或者删除节点向上一直到root依次调用,一种是按照后续遍历把所有节点都依次调用一遍

这里我们来实现后者。

删除GetNearestNoBalanceNode函数,并修改Adjust函数:

void Adjust(TreeNode* root)
{
if (!root)return;
Adjust(root->left);
Adjust(root->right);
Rotate(root);
}

进一步的,Adjust函数和Rotate函数可以融合到一起,减少maxDepth函数的调用次数。

int Adjust(TreeNode* root)
{
if (!root)return 0;
int dl = Adjust(root->left);
int dr = Adjust(root->right);
if(dl>dr+1){
if (maxDepth(root->left->left) > maxDepth(root->left->right))RotateLL(root);
else RotateLR(root);
return dl;
} else if(dr>dl+1){
if (maxDepth(root->right->right) > maxDepth(root->right->left))RotateRR(root);
else RotateRL(root);
return dr;
}
return max(dl,dr)+1;
}

能不能在Adjust函数递归的过程中,记录更多的信息,从而直接抛弃maxDepth函数呢?

这块我没有深入研究,因为当前版本的性能虽然不高但是足够通过下面的力扣 108题了,也就可以校验逻辑正确性了。

4,完整插入代码(版本三)

#include <iostream>
#include<algorithm>
using namespace std;

struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
};

void InsertNode(TreeNode*& root, TreeNode* node)
{
if (!root)root = node;
else if (root->val > node->val)InsertNode(root->left, node);
else InsertNode(root->right, node);
}

int maxDepth(TreeNode* root) {
if (!root)return 0;
return max(maxDepth(root->left), maxDepth(root->right)) + 1;
}

void RotateLL(TreeNode* root)
{
TreeNode* c1 = root->left, * c2 = c1->left;
root->left = c2, c1->left = c1->right, c1->right = root->right, root->right = c1;
int val = root->val;
root->val = c1->val, c1->val = val;
}
void RotateRR(TreeNode* root)
{
TreeNode* c1 = root->right, * c2 = c1->right;
root->right = c2, c1->right = c1->left, c1->left = root->left, root->left = c1;
int val = root->val;
root->val = c1->val, c1->val = val;
}
void RotateLR(TreeNode* root)
{
TreeNode* c1 = root->left, * c2 = c1->right;
root->left = c2, c1->right = c2->left, c2->left = c1;
RotateLL(root);
}
void RotateRL(TreeNode* root)
{
TreeNode* c1 = root->right, * c2 = c1->left;
root->right = c2, c1->left = c2->right, c2->right = c1;
RotateRR(root);
}

int Adjust(TreeNode* root)
{
if (!root)return 0;
int dl = Adjust(root->left);
int dr = Adjust(root->right);
if(dl>dr+1){
if (maxDepth(root->left->left) > maxDepth(root->left->right))RotateLL(root);
else RotateLR(root);
return dl;
} else if(dr>dl+1){
if (maxDepth(root->right->right) > maxDepth(root->right->left))RotateRR(root);
else RotateRL(root);
return dr;
}
return max(dl,dr)+1;
}

void InsertAndAdjust(TreeNode*& root, int val)
{
TreeNode* p = find(root,val);
if(p)return;
p= new TreeNode();
p->val = val;
p->left=NULL, p->right=NULL;
InsertNode(root, p);
Adjust(root);
}

int main()
{
TreeNode* root = NULL;
InsertAndAdjust(root, -10);
InsertAndAdjust(root, -3);
InsertAndAdjust(root, 0);
InsertAndAdjust(root, 5);
InsertAndAdjust(root, 9);
return 0;
}

5,删除节点

基于以上的重构工作,删除节点就变得十分简单了。

平衡二叉树删除节点分两步:

(1)删除一个节点,得到二叉搜索树,代码和二叉搜索树删除节点完全相同

(2)调整深度,得到平衡二叉树,代码和平衡二叉树插入节点(版本三)完全相同

注意:虽然插入和删除节点的调整深度的过程是一样的,都是调用Adjust函数,但是两种场景下的树深度变化规律却是不完全相同

相同点是,都是按照后序遍历依次调整所有节点,都只有不平衡点才有实际调整操作,平衡点啥都不做,不平衡点都是两边子树的深度差为2

不同点是,插入节点后调用Adjust函数,两颗子树的深度分别加1和减1,调整之后两边深度相等,树的深度减1,

删除节点后调用Adjust函数,两颗子树的深度分别加1 和 “减1或不变”,调整之后两边深度相等或相差1,树的深度减1或不变。

PS:调用Adjust函数对子树的影响,取决于代码细节,如果maxDepth(root->left->left) > maxDepth(root->left->right)的条件改成>=,那上面的表述又略有差异。

删除节点完整代码:

TreeNode* GetMin(TreeNode* root)
{
if (!root)return root;
if (root->left)return GetMin(root->left);
return root;
}

TreeNode* GetParent(TreeNode* root, TreeNode* node)
{
if (!root || root == node || root->left == node || root->right == node)return root;
if (root->val > node->val)return GetParent(root->left, node);
return GetParent(root->right, node);
}

void DeleteNode(TreeNode*& node)
{
if (node->left == NULL) {
node = node->right;
return;
}
if (node->right == NULL) {
node = node->left;
return;
}
TreeNode* thenext = GetMin(node->right);
if (thenext == node->right) {
thenext->left = node->left, node = thenext;
return;
}
node->val = thenext->val;
TreeNode* p = GetParent(node, thenext);
p->left = thenext->right;
}

int maxDepth(TreeNode* root) {
if (!root)return 0;
return max(maxDepth(root->left), maxDepth(root->right)) + 1;
}

void RotateLL(TreeNode* root)
{
TreeNode* c1 = root->left, * c2 = c1->left;
root->left = c2, c1->left = c1->right, c1->right = root->right, root->right = c1;
int val = root->val;
root->val = c1->val, c1->val = val;
}
void RotateRR(TreeNode* root)
{
TreeNode* c1 = root->right, * c2 = c1->right;
root->right = c2, c1->right = c1->left, c1->left = root->left, root->left = c1;
int val = root->val;
root->val = c1->val, c1->val = val;
}
void RotateLR(TreeNode* root)
{
TreeNode* c1 = root->left, * c2 = c1->right;
root->left = c2, c1->right = c2->left, c2->left = c1;
RotateLL(root);
}
void RotateRL(TreeNode* root)
{
TreeNode* c1 = root->right, * c2 = c1->left;
root->right = c2, c1->left = c2->right, c2->right = c1;
RotateRR(root);
}

int Adjust(TreeNode* root)
{
if (!root)return 0;
int dl = Adjust(root->left);
int dr = Adjust(root->right);
if(dl>dr+1){
if (maxDepth(root->left->left) > maxDepth(root->left->right))RotateLL(root);
else RotateLR(root);
return dl;
} else if(dr>dl+1){
if (maxDepth(root->right->right) > maxDepth(root->right->left))RotateRR(root);
else RotateRL(root);
return dr;
}
return max(dl,dr)+1;
}

测试代码:

int main()
{
TreeNode* root=NULL;
InsertAndAdjust(root, 10);
InsertAndAdjust(root, 3);
InsertAndAdjust(root, 0);
InsertAndAdjust(root, 5);
InsertAndAdjust(root, 9);
InsertAndAdjust(root, 2);
InsertAndAdjust(root, 4);
InsertAndAdjust(root, 7);
InsertAndAdjust(root, 1);
DeleteNode(root->right);
Adjust(root);
return 0;
}

七,OJ实战

1,平衡无序二叉树

力扣 110. 平衡二叉树

给定一个二叉树,判断它是否是高度平衡的二叉树。

本题中,一棵高度平衡二叉树定义为:

一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。


示例 1:

输入:root = [3,9,20,null,null,15,7]

输出:true

示例 2:

输入:root = [1,2,2,3,3,null,null,4,4]

输出:false

示例 3:

输入:root = []

输出:true


提示:

树中的节点数在范围 [0, 5000] 内

-104 <= Node.val <= 104

代码:

class Solution {
public:
int maxDepth(TreeNode* root) {
if (!root)return 0;
int d1 = maxDepth(root->left), d2 = maxDepth(root->right);
if (d1<0 || d2<0 || d1 - d2 > 1 || d2 - d1 > 1)return -1;
return max(d1,d2) + 1;
}
bool isBalanced(TreeNode* root) {
return maxDepth(root) >= 0;
}
};

2,平衡排序二叉树

力扣 108. 将有序数组转换为二叉搜索树

给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其换为一棵 高度平衡 二叉搜索树。

高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树。


示例 1:

输入:nums = [-10,-3,0,5,9]

输出:[0,-3,9,-10,null,5]

解释:[0,-10,5,null,-3,null,9] 也将被视为正确答案:

示例 2:

输入:nums = [1,3]

输出:[3,1]

解释:[1,3] 和 [3,1] 都是高度平衡二叉搜索树。


提示:

1 <= nums.length <= 10^4

-10^4 <= nums[i] <= 10^4

nums 按 严格递增 顺序排列

代码:

void InsertNode(TreeNode* &root, TreeNode* node)
{
if(!root)root=node;
else if(root->val>node->val)InsertNode(root->left,node);
else InsertNode(root->right,node);
}

//ans是出参,返回值是深度
int GetNearestNoBalanceNode(TreeNode* root, TreeNode* &ans)
{
if(!root)return 0;
int d1=GetNearestNoBalanceNode(root->left,ans);
int d2=GetNearestNoBalanceNode(root->right,ans);
if(d1<0 || d2<0)return -1;
if(d1-d2>1 || d2-d1>1){
ans=root;
return -1;
}
ans = NULL;
return ((d1<d2)?d2:d1)+1;
}

void CC(TreeNode* root, TreeNode* node, TreeNode* &c1, TreeNode* &c2)
{
if(root->val>node->val)c1=root->left;
else c1=root->right;
if(c1->val>node->val)c2=c1->left;
else c2=c1->right;
}

void RotateLL(TreeNode* root, TreeNode* c1, TreeNode* c2)
{
root->left=c2,c1->left=c1->right,c1->right=root->right,root->right=c1;
int val=root->val;
root->val=c1->val,c1->val=val;
}
void RotateRR(TreeNode* root, TreeNode* c1, TreeNode* c2)
{
root->right=c2,c1->right=c1->left,c1->left=root->left,root->left=c1;
int val=root->val;
root->val=c1->val,c1->val=val;
}
void RotateLR(TreeNode* root, TreeNode* c1, TreeNode* c2)
{
root->left=c2,c1->right=c2->left,c2->left=c1;
RotateLL(root,c2,c1);
}
void RotateRL(TreeNode* root, TreeNode* c1, TreeNode* c2)
{
root->right=c2,c1->left=c2->right,c2->right=c1;
RotateRR(root,c2,c1);
}
void Rotate(TreeNode* root, TreeNode* c1, TreeNode* c2)
{
if(root->left==c1){
if(c1->left==c2)RotateLL(root,c1,c2);
else RotateLR(root,c1,c2);
} else{
if(c1->left==c2)RotateRL(root,c1,c2);
else RotateRR(root,c1,c2);
}
}

void Adjust(TreeNode* root, TreeNode* node)
{
TreeNode*p,*c1,*c2;
GetNearestNoBalanceNode(root,p);
if(p==NULL)return;
CC(p,node,c1,c2);
Rotate(p,c1,c2);
}

void InsertAndAdjust(TreeNode* &root, int val)
{
TreeNode* p= new TreeNode();
p->val=val;
p->left=NULL, p->right=NULL;
InsertNode(root,p);
Adjust(root,p);
}

class Solution {
public:
TreeNode* sortedArrayToBST(vector<int>& nums) {
TreeNode*root=NULL;
for(int i=0;i<nums.size();i++)InsertAndAdjust(root, nums[i]);
return root;
}
};

力扣 109. 有序链表转换二叉搜索树

给定一个单链表,其中的元素按升序排序,将其换为高度平衡的二叉搜索树。

本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。

示例:

给定的有序链表: [-10, -3, 0, 5, 9],

一个可能的答案是:[0, -3, 9, -10, null, 5], 它可以表示下面这个高度平衡二叉搜索树:

      0

     / \

   -3   9

   /   /

 -10  5

//省略若干函数,同力扣108

class Solution {
public:
TreeNode* sortedListToBST(ListNode* head) {
TreeNode*root=NULL;
while(head){
InsertAndAdjust(root,head->val);
head=head->next;
}
return root;
}
};