1066 Root of AVL Tree 甲级 xp_xht123

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树是一种自平衡二叉搜索树。在AVL树中,任何节点的两个子树的高度最多相差 1 个。如果某个时间,某节点的两个子树之间的高度差超过 1,则将通过树旋转进行重新平衡以恢复此属性,其次需要搞明白怎么旋转

/*
旋转过后中序遍历不变

右旋:将节点绕左儿子顺时针下压 根节点成为左子节点的右子树

左旋:将节点绕右儿子逆时针下压 根节点成为右儿子点的左子树

第一种情况,需要进行对根的右旋  
将根节点绕左儿子顺时针下压
    A
   /
  B  ->    B
 /        / \
C        C   A

第二种情况,需要进行对根的左旋 
将根节点绕右儿子逆时针下压
A
 \
  B     ->     B
   \          / \
    C        A   C
        
第三种情况,需要先对B左旋,再对根右旋
    A                A
   /                /
  B       ->       C       ->       C
   \              /                / \
    C            B                B   A
    
第四种情况,需要先对B右旋,再对根左旋
  A            A
   \            \
    B    ->      C       ->      C
   /              \             / \
  C                B           A   B
  
*/

这图真是煞费我苦心了

最后需要写出左旋和右旋的操作

void R(int &u)
{//右旋
    int p = v[u].left;//左孩子
    v[u].left = v[p].right;//将 u的左孩子的右孩子 接到 u节点的左边
/*
    A       B
   /       / \
  B  ->   C   A
 / \         /
C   D       D
*/
    v[p].right = u;//u 成为左孩子的右节点
    update(u) , update(p);
    //更新节点的深度
    u = p;//旋完之后根节点就是左孩子
}

void L(int &u)
{//左旋和右旋同理
    int p = v[u].right;
    v[u].right = v[p].left;
    v[p].left = u;
    update(u) , update(p);
    u = p;
}

最后,输出根所对应的值即可

以下是代码:

更新深度函数

void update(int u)
{//更新每一个节点的深度
    h[u] = max(h[v[u].left], h[v[u].right]) + 1;
}

节点深度差函数

//某节点的两个子树之间的高度差超过 1
int height_cha(int u)
{
    return h[v[u].left] - h[v[u].right];
}

插入函数

void insert(int &u , int x)
{
    //初始AVL树中没有节点
    if(!u) u = ++ ind, v[u].val = x;
    else if(x < v[u].val)
    {
        insert(v[u].left , x);//x比根的值要小那么插到左边
        if(height_cha(u) == 2)
        {
            if(height_cha(v[u].left) == 1) R(u);//上述的第一种情况
            else L(v[u].left) , R(u);//上述的第三种情况
        }
    }
    else
    {
        insert(v[u].right , x);//x比根的值要大那么插到右边
        if(height_cha(u) == -2)
        {
            if(height_cha(v[u].right) == -1) L(u);//上述的第二种情况
            else R(v[u].right) , L(u);//上述的第四种情况
        }
    }
    
    update(u);//更新一下根节点的高度
}

以下是完整代码:

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;
const int N = 40;
int n;

struct Node
{
    int val;
    int right;
    int left;
}v[N];

int h[N] , ind;
void update(int u)
{//更新每一个节点的深度
    h[u] = max(h[v[u].left], h[v[u].right]) + 1;
}
/*
旋转过后中序遍历不变

右旋:将节点绕左儿子顺时针下压 根节点成为左子节点的右子树

左旋:将节点绕右儿子逆时针下压 根节点成为右儿子点的左子树

第一种情况,需要进行对根的右旋  
将根节点绕左儿子顺时针下压
    A
   /
  B  ->    B
 /        / \
C        C   A

第二种情况,需要进行对根的左旋 
将根节点绕右儿子逆时针下压
A
 \
  B     ->     B
   \          / \
    C        A   C
        
第三种情况,需要先对B左旋,再对根右旋
    A                A
   /                /
  B       ->       C       ->       C
   \              /                / \
    C            B                B   A
    
第四种情况,需要先对B右旋,再对根左旋
  A            A
   \            \
    B    ->      C       ->      C
   /              \             / \
  C                B           A   B
  
*/
void R(int &u)
{//右旋
    int p = v[u].left;//左孩子
    v[u].left = v[p].right;//将 u的左孩子的右孩子 接到 u节点的左边
/*
    A       B
   /       / \
  B  ->   C   A
 / \         /
C   D       D
*/
    v[p].right = u;//u 成为左孩子的右节点
    update(u) , update(p);
    //更新节点的深度
    u = p;//旋完之后根节点就是左孩子
}

void L(int &u)
{//左旋和右旋同理
    int p = v[u].right;
    v[u].right = v[p].left;
    v[p].left = u;
    update(u) , update(p);
    u = p;
}

//某节点的两个子树之间的高度差超过 1
int height_cha(int u)
{
    return h[v[u].left] - h[v[u].right];
}

void insert(int &u , int x)
{
    //初始AVL树中没有节点
    if(!u) u = ++ ind, v[u].val = x;
    else if(x < v[u].val)
    {
        insert(v[u].left , x);//x比根的值要小那么插到左边
        if(height_cha(u) == 2)
        {
            if(height_cha(v[u].left) == 1) R(u);//上述的第一种情况
            else L(v[u].left) , R(u);//上述的第三种情况
        }
    }
    else
    {
        insert(v[u].right , x);//x比根的值要大那么插到右边
        if(height_cha(u) == -2)
        {
            if(height_cha(v[u].right) == -1) L(u);//上述的第二种情况
            else R(v[u].right) , L(u);//上述的第四种情况
        }
    }
    
    update(u);//更新一下根节点的高度
}

int main()
{
    cin >> n;
    int root = 0;
    while(n --)
    {
        int num;
        cin >> num;
        insert(root , num);
    }
    
    cout<<v[root].val;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值