1066 Root of AVL Tree (25分)

110 篇文章 0 订阅
55 篇文章 1 订阅

An AVL tree is a self-balancing binary search tree. In an AVL tree, the heights of the two child subtrees of any node differ by at most one; if at any time they differ by more than one, rebalancing is done to restore this property. Figures 1-4 illustrate the rotation rules.

在这里插入图片描述

Now given a sequence of insertions, you are supposed to tell the root of the resulting AVL tree.
Input Specification:
Each input file contains one test case. For each case, the first line contains a positive integer N (≤20) which is the total number of keys to be inserted. Then N distinct integer keys are given in the next line. All the numbers in a line are separated by a space.

Output Specification:
For each test case, print the root of the resulting AVL tree in one line.

Sample Input 1:
5
88 70 61 96 120
Sample Output 1:
70
Sample Input 2:
7
88 70 61 96 120 90 65
Sample Output 2:
88

这是一道avl建树的问题,总算自己实现了一下。
avl树就是二叉平衡搜索树,它会自动实现结点的旋转,保证每棵树的左右深度差不超过1,同时具有二叉搜索树的性质,所以在建树时要考虑这两点。
我们按步骤来捋一捋这个建树模板
先获取原始序列,定义一个空结点作为根结点,node* root = nullptr。
然后是buildTree函数,此函数返回值是指向node类型的指针,遍历序列中的所有点,将这些点insert进root下,所以insert需要两个参数,一个是插入结点,一个是插入值。
因为insert是递归函数,所有一定有递归边界,递归边界就是如果当前结点为空结点,就root = new node,不需要*(不太懂),因为对于空结点我们不能做任何操作,将插入值赋给root,并将其左右子树设为空。递归式(当前不考虑平衡问题,只考虑二叉搜索树的性质),为如果插入值大于当前结点的值,就插入当前结点的右子树,反之插入左子树。
如果是一棵二叉搜索树,那么你已经完成了,但是你要考虑平衡旋转结点的操作,一共有四种旋转操作
第一种是左旋:针对的情况是当前结点插在根结点的右子树的右子树,这样给它直接来个大型的左旋就行了,左旋就是将根结点的右节点设为现在的根结点,并将原来的根结点设为现在根结点的左子树,右子树保持不变。对应的代码是,node* t = 根结点的右子树,所以根节点的右子树现在是空了,我们将t的左子树赋给根结点的右子树(因为t的左子树等下要被重新赋值了),最后将t的左子树设为root。
第二种是右旋:和左旋差不多
第三种是先右旋再左旋:(我们要注意,最后进行的是左旋,所以这是针对右子树出问题的情况),如果是右子树的右子树出问题,只要左旋就行了,所以这里是右子树的左子树出问题,我们只要对root->rchild进行右旋操作,这样就是和左旋一样的问题了,所以只要return对root进行左旋即可。
第四种是先左旋再右旋:同上
接下来我们要考虑什么时候进行上述的操作,就是在插入结点之后,因为是递归式,在插入完成后会返回上一层(可以判断当前结点的父结点是否不平衡了),是否不平衡自己写一个depth求树深度。
再插入右子树之后,我们要判断的是我们是否插在右子树的右子树了,如果插在右子树的右子树,只要进行左旋即可,如果不是,就进行先右旋后左旋。如何判断,只需要判断插入值value和当前根结点的右子结点哪个大即可,如果value小于它,那么就直接左旋,反之右旋左旋。直接使root等于旋转函数返回值即可。

root = value > root->rchild->val ? rotateLeft(root) : rotateRightLeft(root);

插入左子树的话,进行相反的判断即可
最后输出根结点的值

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

struct node{
    int val;
    node *lchild, *rchild;
};

vector<int> order;

node* rotateLeft(node* root){//左旋操作
    node* t = root->rchild;
    root->rchild = t->lchild;
    t->lchild = root;
    return t;
}

node* rotateRight(node* root){//右旋操作
    node* t = root->lchild;
    root-> lchild = t->rchild;
    t->rchild = root;
    return t;
}

node* rotateLeftRight(node* root){
    root->lchild = rotateLeft(root->lchild);
    return rotateRight(root);
}

node* rotateRightLeft(node* root){
    root->rchild = rotateRight(root->rchild);
    return rotateLeft(root);
}

int depth(node* root){
    if(root == nullptr) return 0;
    return max(depth(root->lchild), depth(root->rchild))+1;
}

void insert(node* &root, int value){//无需返回值,只需要修改初始的root结点即可,传引用
    if(root == nullptr){//到空结点了,可以开插了
        root = new node;//无需*
        root->val = value;
        root->lchild = root->rchild = nullptr;
    }
    else if(value >= root->val){
        insert(root->rchild, value);
        if(depth(root->rchild) - depth(root->lchild) == 2){
            root = value > root->rchild->val ? rotateLeft(root) : rotateRightLeft(root);
        }
    }
    else{
        insert(root->lchild, value);
        if(depth(root->lchild) - depth(root->rchild) == 2){
            root = value < root->lchild->val ? rotateRight(root) : rotateLeftRight(root);
        }
    }
}
node* buildTree(node* root){
    for(int i = 0;i < order.size();i++){
        insert(root, order[i]);
    }
    return root;
}

int main(){
    int n;
    cin>>n;
    order.resize(n+1);
    for(int i = 0; i < n; i++){
        cin>>order[i];
    }
    node* root = nullptr;
    root = buildTree(root);
    cout<<root->val;
}

每次都把四种情况画出来就行了。

#include<iostream>
#include<vector>
#include<cmath>
using namespace std;
int n;
vector<int> order;
struct node{
    int val;
    node* lchild, *rchild;
};

node* leftRotate(node* root){//root的右子树变为root的父亲,root在自己左子树的右边,左子树的右子树现在变成root的左子树
    node* temp = root->rchild;
    root->rchild = temp->lchild;
    temp->lchild = root;
    return temp;
}

node* rightRotate(node* root){//root的左子树变为root的父亲,root在自己右子树的左边,右子树的左子树现在变成root的右子树
    node* temp = root->lchild;
    root->lchild = temp->rchild;
    temp->rchild = root;
    return temp;
}

node* rightLeftRotate(node* root){//先右旋再左旋
    //对右子结点先右旋,再整体左旋
    node* temp = rightRotate(root->rchild);
    root->rchild = temp;
    return leftRotate(root);
}

node* leftRightRotate(node* root){//先左旋再右旋
    //对左子结点进行左旋,再整体右旋
    node* temp = leftRotate(root->lchild);
    root->lchild = temp;
    return rightRotate(root);
}

int getHeight(node* root){
    if(root == nullptr) return 0;
    return max(getHeight(root->lchild), getHeight(root->rchild))+1;
}

node* insert(node* root, int val){
    if(root == nullptr){
        node* root = new node;
        root->val = val;
        root->lchild = root->rchild = nullptr;
        return root;
    }
    else if(root->val > val){//插在左子树,所以可能是右旋or左右旋
        root->lchild = insert(root->lchild,val);
        if(abs(getHeight(root->lchild) - getHeight(root->rchild)) > 1){
            //左子树出问题了,看是连续插在左子树还是先插左再插右,连续插左就普通右旋,先左再右就左右旋
            root = val > root->lchild->val ? leftRightRotate(root) : rightRotate(root);
        }
    }
    else{
        root->rchild = insert(root->rchild,val);
        if(abs(getHeight(root->lchild) - getHeight(root->rchild)) > 1){
            //右子树出问题,一定是左旋
            root = val < root->rchild->val ? rightLeftRotate(root) : leftRotate(root);
        }
    }
    return root;
}

node* build(node* root){
    for(int i = 0; i< n; i++){
        root = insert(root,order[i]);
    }
    return root;
}

int main(){
    cin>>n;
    int x;
    for(int i = 0; i < n; i++){
        cin>>x;
        order.push_back(x);
    }
    node* root = nullptr;
    root = build(root);
    cout<<root->val;
    return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值