PAT 1066 Root of AVL Tree C++版
1.题意
给出一个整数序列,现在需要让你输出以这个序列构建得到的AVL树的根节点。
2.分析
之前我一直都是按照个人的理解做AVL ,对于考试题来说,则是相当简单的,但是如果让我单独实现一棵AVL树,我之前是不会的。但是今天看到晴神的书之后,恍然大悟,又进一步理解到**“算法只不过是一种抽象”**,而代码又只是将这种抽象实现起来罢了。
实现AVL的主要步骤如下:
- step1:建树
- setp2:平衡(左右旋)
这里麻烦的地方就是平衡(即左右旋了)。但是如果我们再次抽象的看整个平衡过程,就是左右旋两种情况的组合罢了,这样就得到了四种旋转过程(左旋;右旋;先左旋,再右旋;先右旋,再左旋)。意思就是我们只需要单独实现一个左右旋,然后组合起来,当需要旋转的时候,调用函数即可。
而在左右旋之前,我们需要判断二叉树是否失衡。这就涉及到求当前节点高度getHeight()
,以及判断平衡因子getBF()
两个步骤。
而在每次旋转之后(或者是插入新节点之后),我们都需要对影响到的树中节点的高度进行更新updateHeight()
。
根据上面的分析,得到如下的代码
3.代码
#include<cstdio>
#include<iostream>
#define maxn 100
using namespace std;
int N ;
struct Node{
Node* left;
Node* right;
int data;//数值
int height;//高度
};
Node* newNode(int v){
Node* node = new Node;//构建一个新节点
node->left = NULL;
node->right = NULL;
node->data = v;//
node->height = 1;//高度为1
return node;
}
//更新节点高度
void updateHeight(Node* root){
int lh ;
int rh;
if(root->left==NULL) lh = 0;
else lh=root->left->height;
if(root->right == NULL) rh = 0;
else rh=root->right->height;
root->height = max(lh,rh)+1;
}
//获取当前root 节点的高度
int getHeight(Node* root){
if(root== NULL) return 0;
return root->height;
}
//计算节点root的平衡因子
int getBF(Node* root){
return getHeight(root->left) - getHeight(root->right);
}
//右旋
void R(Node* &root){
Node* temp = root->left;
root->left = temp->right;
temp->right = root;
updateHeight(root);
updateHeight(temp);
root = temp;//以temp作为根
}
//左旋
void L(Node* &root){
Node* temp = root->right;
root->right = temp->left;
temp->left = root;
updateHeight(root);
updateHeight(temp);
root = temp;//以temp作为根
}
//插入值
void insert(Node* &root,int v){
if(root == NULL){//如果root 为NULL
root = newNode(v);
return;
}
if(v > root->data ){//进入右子树
insert(root->right,v) ;
updateHeight(root);//因为进入了右子树,所以需要更新 当前root的高度
if(getBF(root) == -2){//说明右子树高
if(getBF(root->right) == -1) L(root);
else{
R(root->right);
L(root);
}
}
}
else {//进入左子树
insert(root->left,v) ;
updateHeight(root);
if(getBF(root) == 2){//左子树高
if(getBF(root->left) == 1) {//LL型
//直接右旋
R(root);
}
else{//LR型
//先左旋,再右旋
L(root->left);
R(root);
}
}
}
}
int main(){
cin >> N;
int v;//当前需要插入的值
int i,j;
Node* root = NULL;//作为根节点
for(i = 0;i< N;i++){
cin >> v;
insert(root,v) ;
}
cout << root->data;
}
4.测试用例
3
88 70 61
5
88 70 61 96 120
7
88 70 61 96 120 90 65
5.执行结果
开心的事儿就是一次AC啦,啦啦啦啦
6.注意点
-
LL,LR,RL, RR
代表的都是树型,而不是左右旋!!LR代表的就是树型先往左偏,再往右偏的情况。如下图所示:
RR代表的就是全都右偏的树型。如下所示:
其它的类似,这里不再分析。 -
在调整树型的时候,需要注意,如果是LL,或者是RR型,直接对
root
进行右旋或者左旋;但是如果树型是LR,或者是RL,则需要先对root的左子树,或是root的右子树进行旋转,然后再对root进行旋转。 -
最后给出一副手绘图,用以表示左右旋的过程。