题目大意:
给定N个结点,要求根据这N个结点构建AVL树,然后输出根节点的权值。
算法思路:
此题主要考察的就是AVL的建树过程,AVL树的构建过程本质和二叉搜索树完全一致,只不过在其基础上添加了平衡因子,需要对不平衡的情况进行左旋和右旋,所以得计算结点的平衡因子,又由于平衡因子的计算依赖左子树和右子树的高度,所以得在结点中新增高度height属性和获取结点高度的函数getHeight,这样就可以使用getBalance获得节点的平衡因子,最后由于在构建AVL树的过程中涉及到树的旋转过程,会改变树中某些节点的高度,所以得重新计算结点高度,这里使用calHeight函数来实现。其次,AVL树的不平衡状态有四种,如下所示;
- 1.LL:在左子树的左孩子上插入结点,导致结点失衡,$getBalance(root)=2$,$getBalance(root->left)=1$,解决方法:右旋$root$
- 2.RR:在右子树的右孩子上插入结点,导致结点失衡,$getBalance(root)=-2$,$getBalance(root->right)=-1$,解决方法:左旋$root$
- 3.LR:在左子树的右孩子上插入结点,导致结点失衡,$getBalance(root)=2$,$getBalance(root->left)=-1$,解决方法:先左旋$root->left$,再右旋$root$
- 4.RL:在右子树的左孩子上插入结点,导致结点失衡,$getBalance(root)=-2$,$getBalance(root->right)=1$,解决方法:右旋$root->right$,再左旋$root$
接下来就是左旋和右旋操作:
- 左旋就是得将根结点的右孩子temp设置为temp的左孩子,然后再设置temp的左孩子为根结点,最后更新根结点root=temp
- 右旋就是得将根结点的左孩子temp设置为temp的右孩子,然后再设置temp的右孩子为根结点,最后更新根结点root=temp
那么在什么时机进行旋转呢?
很显然,在当前节点的左子树或者右子树插入完节点后,当前节点出现不平衡的状态就需要进行旋转了,也即是说,在每次插入节点完毕后,就得重新计算当前节点的高度,然后根据当前节点和左右孩子平衡因子的4种不同情况就进行不同的旋转操作。
不平衡的节点有很多,选择哪一个进行旋转?
这里给出结论,只要把离插入结点最近的失衡结点调整到平衡状态,所有其他节点也会平衡,也就是说我们每次在根节点root的孩子结点上进行插入的时候,只需判断根节点root的平衡因子就好,这里root之所以是最近的非平衡结点,是因为在插入过程中采用递归的写法,每次找到插入位置也就是NULL位置的时候,会向上沿着查找路径进行返回,直到遇到结点平衡因子为2或者-2的,那么此root就是最近的非平衡结点。
注意点:
- 1、如果出现段错误,那么就有可能是新建结点没有返回值,或者是哪的函数没有返回值(主要是地址)
- 2、初始新建的节点,高度得赋值为1.
提交结果:
AC代码:
#include <cstdio>
#include <algorithm>
using namespace std;
struct Node{
int data;
struct Node* left;
struct Node* right;
int height;
};
// 获取当前节点的高度
int getHeight(Node* root){
if(root== nullptr) return 0;
return root->height;
}
// 获得当前节点的平衡因子
int getBalance(Node* root){
return getHeight(root->left) - getHeight(root->right);
}
// 在树形发生改变的时候重新计算节点的高度
int calHeight(Node* root){
return max(getHeight(root->left),getHeight(root->right))+1;
}
// 右旋
void rotation_R(Node* &root){
Node* temp = root->left;//暂存左节点地址
root->left = temp->right;
temp->right = root;
// 树形发生变化,得更新树的节点高度。
root->height = calHeight(root);
temp->height = calHeight(temp);
root = temp;// 更新根节点
}
// 左旋
void rotation_L(Node* &root){
Node* temp = root->right;//暂存右节点地址
root->right = temp->left;
temp->left = root;
// 树形发生变化,得更新树的节点高度。
root->height = calHeight(root);
temp->height = calHeight(temp);
root = temp;// 更新根节点
}
void insert(Node* &root,int num){
if(root== nullptr){
// 达到插入位置
root = new Node();
root->data = num;
root->height = 1;
root->left = root->right = nullptr;
return;
}
if(root->data>num){
// 往左子树插入
insert(root->left,num);
// 插入完成后,需要重新计算树中每一个节点的高度
root->height = calHeight(root);
// 判断当前节点是否失衡,左子树插入节点,只有可能是LR和RR型
if(getBalance(root)==2){
if(getBalance(root->left)==1){
//LL型,右旋root即可
rotation_R(root);
} else if(getBalance(root->left)==-1){
//LR型,得先左旋root->left,再右旋root
rotation_L(root->left);
rotation_R(root);
}
}
} else {
// 往右子树插入
insert(root->right,num);
// 插入完成后,需要重新计算树中每一个节点的高度
root->height = calHeight(root);
// 判断当前节点是否失衡,左子树插入节点,只有可能是RL和LL型
if(getBalance(root)==-2){
if(getBalance(root->right)==-1){
// RR型,左旋root即可
rotation_L(root);
} else if(getBalance(root->right)==1){
// RL型,先右旋root->right,再左旋root
rotation_R(root->right);
rotation_L(root);
}
}
}
}
int main()
{
int N;// 节点个数
scanf("%d",&N);
int num;
Node* root = nullptr;
for (int i = 0; i < N; ++i) {
scanf("%d",&num);
insert(root,num);
}
printf("%d",root->data);
return 0;
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。