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