问题 A: 算法9-9~9-12:平衡二叉树的基本操作
题目描述
平衡二叉树又称AVL树,它是一种具有平衡因子的特殊二叉排序树。平衡二叉树或者是一棵空树,或者是具有以下几条性质的二叉树:
1. 若它的左子树不空,则左子树上所有结点的值均小于它的根节点的值;
2. 若它的右子树不空,则右子树上所有结点的值均大于它的根节点的值;
3. 它的左右子树也分别为平衡二叉树,且左子树和右子树的深度之差的绝对值不超过1。
若将二叉树上结点的平衡因子定义为该结点的左子树深度减去它的右子树的深度,则平衡二叉树上的所有结点的平衡因子只可能为-1、0和1。只要二叉树上有一个结点的平衡因子的绝对值大于1,则这棵二叉树就是不平衡的。
通过平衡二叉树的性质不难得知,其插入、删除、查询都操作的时间复杂度均为O(log2n)。
维持平衡二叉树性质的操作可以被称为旋转。其中平衡二叉树的右旋处理可以描述如下:
而其左旋处理与右旋正好相反,可以描述如下:
在本题中,读入一串整数,首先利用这些整数构造一棵平衡二叉树。另外给定多次查询,利用构造出的平衡二叉树,判断每一次查询是否成功。
输入
输入的第一行包含2个正整数n和k,分别表示共有n个整数和k次查询。其中n不超过500,k同样不超过500。
第二行包含n个用空格隔开的正整数,表示n个整数。
第三行包含k个用空格隔开的正整数,表示k次查询的目标。
输出
只有1行,包含k个整数,分别表示每一次的查询结果。如果在查询中找到了对应的整数,则输出1,否则输出0。
请在每个整数后输出一个空格,并请注意行尾输出换行。
样例输入
<span style="color:#333333">8 3
1 3 5 7 8 9 10 15
9 2 5
</span>
样例输出
<span style="color:#333333">1 0 1
</span>
提示
在本题中,首先需要按照题目描述中的算法完成平衡二叉树的构造过程,之后需要通过在平衡二叉树中的不断向下查找,将需要查询的值与当前节点的值进行比较,直到确定被查询的值是否存在。
通过课本中的性能分析部分,不难发现平衡二叉树的查找、插入、删除等操作的时间复杂度均为O(log2n),这是通过利用旋转操作使二叉树保持平衡状态而保证的。
参考代码:
#include <cstdio>
#include <algorithm>
using namespace std;
//AVL 树
//1、结点
struct node{
int v,height;//v为结点权值,height为当前子树高度
node *lchild,*rchild ;
};
//新建结点
node * newnode(int v)
{
node* Node=new node;
Node->v=v;
Node->height=1; //初始高度为1
Node->lchild=Node->rchild=NULL;
return Node;
}
//获取以root为根的子树的当前height
int getheight(node* root)
{
if(root==NULL)
return 0;
else
return root->height;
}
//计算结点root的平衡因子
int getbalance(node *root)
{
return getheight(root->lchild)-getheight(root->rchild);
}
//更新结点root 的height
void updateheight(node* root)
{
root->height=max(getheight(root->lchild),getheight(root->rchild))+1;
}
//基本操作
//1 查找
bool search(node* root,int x)
{
if(root==NULL)
{
//printf("search faild\n");
return false;
}
if(x==root->v)
//printf("%d\n",root->v);
return true;
else if(root->v<x)
search(root->rchild,x);
else
search(root->lchild,x);
}
//2 插入操作
//1)左旋
void L(node* &root)
{
node* temp=root->rchild; //root指向结点A,temp指向结点B
root->rchild=temp->lchild; //1让B的左子树成为 A的右子树
temp->lchild=root;//2 让A成为B的左子树
updateheight(root);//更新结点A的高度
updateheight(temp); //更新结点B的高度
root=temp; //3、将根节点设定为B
}
//2)右旋
void R(node * &root)
{
node*temp=root->lchild;
root->lchild=temp->rchild;
temp->rchild=root;
updateheight(root);
updateheight(temp);
root=temp;
}
//插入权值为v的结点
void insert(node* &root,int v)
{
if(root==NULL) //到达空结点
{
root=newnode(v);
return;
}
if(v<root->v)
{
insert(root->lchild,v);//往左子树插入
updateheight(root);//更新树高
if(getbalance(root)==2)
{
if(getbalance(root->lchild)==1) //LL型
{
R(root);//右旋
}
else if(getbalance(root->lchild)==-1) //LR型
{
L(root->lchild);//先将根节点的左子树左旋
R(root); //再将根节点右旋
}
}
}
else
{
insert(root->rchild,v);//往右子树插入
updateheight(root);
if(getbalance(root)==-2)//不平衡,需调整
{
if(getbalance(root->rchild)==-1)//RR型
L(root);
else if(getbalance(root->rchild)==1)//RL型
{
R(root->rchild);//先将根节点右子树右旋
L(root); //再将根节点左旋
}
}
}
}
//AVL树的建立
/*node *create(int data[],int n)
{
node* root=NULL;//新建空根结点root
for(int i=0;i<n;++i)
{
insert(root,data[i]);
}
return root;
}*/
int main()
{
int n,m,v,s;
node* root=NULL;
scanf("%d%d",&n,&m);
for(int i=0;i<n;++i)
{
scanf("%d",&v);
insert(root,v);
}
for(int i=0;i<m;++i)
{
scanf("%d",&s);
if(search(root,s))
printf("1 ");
else
printf("0 ");
}
return 0;
}