PAT_甲级_1066 Root of AVL Tree

题目大意:

给定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

image.png

  • 右旋就是得将根结点的左孩子temp设置为temp的右孩子,然后再设置temp的右孩子为根结点,最后更新根结点root=temp

image.png

那么在什么时机进行旋转呢?

很显然,在当前节点的左子树或者右子树插入完节点后,当前节点出现不平衡的状态就需要进行旋转了,也即是说,在每次插入节点完毕后,就得重新计算当前节点的高度,然后根据当前节点和左右孩子平衡因子的4种不同情况就进行不同的旋转操作。

不平衡的节点有很多,选择哪一个进行旋转?

这里给出结论,只要把离插入结点最近的失衡结点调整到平衡状态,所有其他节点也会平衡,也就是说我们每次在根节点root的孩子结点上进行插入的时候,只需判断根节点root的平衡因子就好,这里root之所以是最近的非平衡结点,是因为在插入过程中采用递归的写法,每次找到插入位置也就是NULL位置的时候,会向上沿着查找路径进行返回,直到遇到结点平衡因子为2或者-2的,那么此root就是最近的非平衡结点。

注意点:

  • 1、如果出现段错误,那么就有可能是新建结点没有返回值,或者是哪的函数没有返回值(主要是地址)
  • 2、初始新建的节点,高度得赋值为1.

提交结果:

image.png

AC代码:

#include 
#include 

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;
}

你可能感兴趣的