9.5.1 平衡二叉树的定义

上一节的二叉二叉查找树BST,若按照顺序插入,会退化为一条链,eg:使用序列{1,2,3,4,5}构建二叉树,会得到

算法笔记9.5 平衡二叉树(AVL树)_子树

于是需要对树的结构进行调整,使得树的高度在每次插入元素后仍然能保持O(logn)的级别,这样就产生了平衡二叉树。

所谓平衡二叉树是指:对AVL树的任意结点来说,其左子树与右子树的高度之差的绝对值不超过1(左右子树的高度之差称为该结点的平衡因子

算法笔记9.5 平衡二叉树(AVL树)_平衡二叉树_02

定义及结点高度的操作

定义结构中加一个变量height,用来记录以当前结点为根结点的子树的高度

//平衡二叉树的定义 struct node{ int v,height;//v为结点的权值,height为当前子树的高度 node *lchild,*rchild;//左右孩子结点 }; //新建结点 //生成一个新结点,v为结点权值 node* newNode(int v){ node* Node=new node; Node->data=v; Node->height=1;//注意初始高度为1 有v一定不空 Node->lchild=Node->rchild=NULL; return Node; } //封装一下求子树root的高度 int getHeight(node* root){ if(root==NULL) return 0; else return root->height;//等于NULL又使用->会空指针的 } //计算平衡因子 int getBlanceFactor(node* root){ //左子树高度减去右子树高度 return getHeight(root->lchild)-getHeight(root->rchild); } //显然当前结点为根的子树高度(即当前结点的height)等于左子树的height与右子树的height的较大值+1 //更新结点的height void updateHeight(node* root){ //max(左孩子height,右孩子height)+1 root->height=max(getHeight(root->rchild),getHeight(root->lchild))+1; }

 

 

9.5.2平衡二叉树的基本操作

查找、插入、建立

1.查找操作

AVL(平衡二叉树)也是一个二叉查找树,查找操作和BST一样。但是AVL的高度为O(logn),查找时间复杂度为O(logn)

//AVL查找操作 和BST完全相同
void search(node* root,int x){
if(root==NULL) return NULL;
if(root->data==x) cout<<root->data<<endl;//找到访问之
if(root->data>x) search(root->lchild,x);
else search(root->rchild,x);
}

 

2.插入操作

左旋

算法笔记9.5 平衡二叉树(AVL树)_子树_03

算法笔记9.5 平衡二叉树(AVL树)_结点_04

//左旋代码

//插入操作
//左旋
void L(node* &root){
node* temp=root->rchild;//root指向根A temp指向右结点B
root->rchild=temp->lchild;//B的左子树成为A的右子树
temp->lchild=root;//A成为B的左子树
//更新A、B的高度不能忘了 AVL的保证
updateHeight(root);//更新A的高度
updateHeight(temp);//更新B的高度
//除了A、B,其他子树都没有变化,不用改东西
root=temp;//根结点设为B
}

 

//右旋

算法笔记9.5 平衡二叉树(AVL树)_平衡二叉树_05

算法笔记9.5 平衡二叉树(AVL树)_结点_06

//右旋代码

//右旋代码
void R(node* &root){
node* temp=root->lchild;//root指向根B temp指向左结点A
root->lchild=temp->rchild;//左子树的右孩子变为根结点的左孩子(开始是,现在还是,大小关系未变)
temp->rchild=root;//根成为左子树temp的右孩子(根大)
//不能忘了更新高度
updateHeight(root);
updateHeight(temp);
root=temp;
}//写完后发现和左旋正好left变right而right变left 充分说明了对称性

开始插入

算法笔记9.5 平衡二叉树(AVL树)_平衡二叉树_07

算法笔记9.5 平衡二叉树(AVL树)_结点_08

算法笔记9.5 平衡二叉树(AVL树)_结点_09

算法笔记9.5 平衡二叉树(AVL树)_平衡二叉树_10

算法笔记9.5 平衡二叉树(AVL树)_结点_11

算法笔记9.5 平衡二叉树(AVL树)_结点_12

skill1:
        平衡因子    RL
          +               L
          -                R

skill2:
        LL        2   1
        LR        2  -1    
        RR      -2  -1
        RL       -2   1
        //都是先2后1  符号根据L+  R-来判断

skill3: 调整操作
        LL        根右旋
        LR        L孩子L旋(变LL),根右旋   操作也是LR
        RR        根左旋
        RL        R孩子R旋(变RR),根左旋    操作也是RL
        //LL与RR完全对称 R->L L->R
        //LR与RL完全对称 R->L L->R 
        //第一个字母决定了本次操作左孩子还是右孩子  L开头只动了左孩子  R开头只动了右孩子
        //两对记住一对即可
 

//正式插入  相比较于BST每次插入之后多了两步:1.更新树高  2.判断失衡类型调整失衡(LL,LR,RR,RL)
//avl树root中插入值为v的结点
void insert(node* &root,int v){
if(root==NULL){
root=newNode(v);
return;
}

if(v<root->data){
insert(root->lchild,v);
updateHeight(root);//更新树高 左子树不为空则其高度会递归更新的 哪个结点的子树插入了,更新哪个结点,递归更新
//检查平衡态
if(getBlanceFactor(root)==2){//L (BF>1>0 肯定左边长了导致失衡 接下来判断左子树即可)
if(getBlanceFactor(root->lchild)==1){//LL 一次右旋即可
R(root);//下面直接也就return了 不用多写return了
}else if(getBlanceFactor(root->lchild)==-1){//LR 左子树左旋再根右旋 明确写出条件,直接else也行吧 只可能-1 1不会是0
L(root->lchild);
R(root);
}
}
}else{//往右边插
insert(root->rchild,v);
updateHeight(root);
//检查平衡状态 和上面完全对称
if(getBlanceFactor(root)==-2){//R 往右边插 不平衡只可能是负值 RR或RL
if(getBlanceFactor(root->rchild)==-1){//RR
L(root);
}else if(getBlanceFactor(root->rchild)==1){//RL
R(root->rchild);
L(root);
}
}
}
}

 

3.AVL的建立

插入写好了,建立就简单了

node* Create(int data[],int n){
node* root=NULL;//只能赋值NULL 不能赋别的
//插入就是在NULL处插的
for(int i=0;i<n;i++){
insert(root,data[i]);
}
return root;
}

 

综合实例

以{1,2,3,4,5,6,7,8}为插入序列建立一棵AVL,然后再插入9和6   每次插入都层序遍历一次

 

算法笔记9.5 平衡二叉树(AVL树)_结点_13

 

插入9

算法笔记9.5 平衡二叉树(AVL树)_结点_14

再插入6

算法笔记9.5 平衡二叉树(AVL树)_平衡二叉树_15

综合代码

#include<iostream>
#include<queue>
using namespace std;

//平衡二叉树的定义
struct node{
int data,height;//v为结点的权值,height为当前子树的高度
node *lchild,*rchild;//左右孩子结点
};

//新建结点
//生成一个新结点,v为结点权值
node* newNode(int v){
node* Node=new node;
Node->data=v;
Node->height=1;//注意初始高度为1 有v一定不空
Node->lchild=Node->rchild=NULL;
return Node;
}

//封装一下求子树root的高度
int getHeight(node* root){
if(root==NULL) return 0;
else return root->height;//等于NULL又使用->会空指针的
}

//计算平衡因子
int getBlanceFactor(node* root){
//左子树高度减去右子树高度
return getHeight(root->lchild)-getHeight(root->rchild);
}

//显然当前结点为根的子树高度(即当前结点的height)等于左子树的height与右子树的height的较大值+1
//更新结点的height
void updateHeight(node* root){
//max(左孩子height,右孩子height)+1 新结点NULL->newNode 即0->1 然后依次递归+1
root->height=max(getHeight(root->rchild),getHeight(root->lchild))+1;
}

//AVL查找操作 和BST完全相同
void search(node* root,int x){
if(root==NULL) return;
if(root->data==x) cout<<root->data<<endl;//找到访问之
if(root->data>x) search(root->lchild,x);
else search(root->rchild,x);
}

//插入操作
//左旋
void L(node* &root){
node* temp=root->rchild;//root指向根A temp指向右结点B
root->rchild=temp->lchild;//B的左子树成为A的右子树
temp->lchild=root;//A成为B的左子树
//更新A、B的高度不能忘了 AVL的保证
updateHeight(root);//更新A的高度
updateHeight(temp);//更新B的高度
//除了A、B,其他子树都没有变化,不用改东西
root=temp;//根结点设为B
}

//右旋代码
void R(node* &root){
node* temp=root->lchild;//root指向根B temp指向左结点A
root->lchild=temp->rchild;//左子树的右孩子变为根结点的左孩子(开始是,现在还是,大小关系未变)
temp->rchild=root;//根成为左子树temp的右孩子(根大)
//不能忘了更新高度
updateHeight(root);
updateHeight(temp);
root=temp;
}//写完后发现和左旋正好left变right而right变left 充分说明了对称性

//正式插入 相比较于BST每次插入之后多了两步:1.更新树高 2.判断失衡类型调整失衡(LL,LR,RR,RL)
//avl树root中插入值为v的结点
void insert(node* &root,int v){
if(root==NULL){
root=newNode(v);
return;
}

if(v<root->data){
insert(root->lchild,v);
updateHeight(root);//更新树高 左子树不为空则其高度会递归更新的 哪个结点的子树插入了,更新哪个结点,递归更新
//检查平衡态
if(getBlanceFactor(root)==2){//L (BF>1>0 肯定左边长了导致失衡 接下来判断左子树即可)
if(getBlanceFactor(root->lchild)==1){//LL 一次右旋即可
R(root);//下面直接也就return了 不用多写return了
}else if(getBlanceFactor(root->lchild)==-1){//LR 左子树左旋再根右旋 明确写出条件,直接else也行吧 只可能-1 1不会是0
L(root->lchild);
R(root);
}
}
}else{//往右边插
insert(root->rchild,v);
updateHeight(root);
//检查平衡状态 和上面完全对称
if(getBlanceFactor(root)==-2){//R 往右边插 不平衡只可能是负值 RR或RL
if(getBlanceFactor(root->rchild)==-1){//RR
L(root);
}else if(getBlanceFactor(root->rchild)==1){//RL
R(root->rchild);
L(root);
}
}
}
}

node* Create(int data[],int n){
node* root=NULL;//只能赋值NULL 不能赋别的
//插入就是在NULL处插的
for(int i=0;i<n;i++){
insert(root,data[i]);
}
return root;
}

//层序遍历
void LayerOrder(node* root){
queue<node*> q;
q.push(root);
while(!q.empty()){
node* top=q.front();
q.pop();
cout<<top->data<<" ";
if(top->lchild!=NULL) q.push(top->lchild);
if(top->rchild!=NULL) q.push(top->rchild);
}
cout<<endl;
}


int main(){
const int n=8;
int data[]={1,2,3,4,5,6,7,8};
node* root=Create(data,n);
cout<<"开始:";LayerOrder(root);
insert(root,9);
cout<<"插入9:";LayerOrder(root);
insert(root,6);
cout<<"再插入6:";LayerOrder(root);
return 0;
}

算法笔记9.5 平衡二叉树(AVL树)_子树_16