平衡二叉樹(AVL樹)定義與基本操作


平衡二叉樹仍然是一棵二叉查找樹,只是在其基礎上增加了“平衡”要求
平衡是指:對AVL樹的任意結點來說,其左子樹與右子樹的高度之差的絕對值不超過1
其中左子樹與右子樹的高度之差稱為該結點的平衡因子
由於需要對每個結點都得到平衡因子,因此需要在樹的結構中加入一個變量height,用以記錄以當前結點為根結點的子樹的高度

1、平衡二叉樹的定義

1.1、存儲結構

struct node{
    int data,height; //data為結點權值,height為當前結點高度
    node* lchild,*rchild;  //左右孩子結點地址
};

 

1.2、新建一個結點

node* newNode(int v)
{
    node* Node=new node;  //申請一個node型變量的地址空間
    Node->data=v;   
    Node->height=1;   //結點高度初始化為1
    Node->lchild=Node->rchild=NULL;  //初始狀態下沒有左右孩子
    return Node;
}

 

1.3、獲取結點root所在子樹的當前高度

int getHeight(node* root)
{
    if(root==NULL) return 0; //空結點,高度為0
    return root->height;
}

 

1.4、計算結點root的平衡因子

int getBalanceFactor(node* root)
{
    //左子樹高度減右子樹高度
    return getHeight(root->lchild)-getHeight(root->rchild);
}

 

1.5、更新結點高度

//結點root所在子樹的height等於其左子樹的height與右子樹的height的較大值加1
void updateHeight(node* root)
{
    root->height=max(getHeight(root->lchild),getHeight(root->rchild))+1;
}

 

 

2、平衡二叉樹的基本操作

2.1、查找操作

//由於AVL樹高度為O(logn)級別,因此AVL樹的查找操作時間復雜度為O(logn)
//search函數查找AVL樹中數據域為x的結點
void search(node* root,int x)
{
    if(root==NULL){   //空樹,查找失敗
        printf("search failed!\n");
        return;
    }
    if(x==root->data){
        printf("%d\n",root->data);
    }else if(x<root->data){
        search(root->lchild,x);  //往左子樹搜索x
    }else{
        search(root->rchild,x);
    }
}

 

2.2、左旋

 

 

/*
①讓B的左子樹成為A的右子樹
②讓A成為B的左子樹
③將根結點設為結點B
*/
void L(node* &root)
{
     node* temp=root->rchild;
     root->rchild=temp->lchild;
     temp->lchild=root;
     updateHeight(root);
     updateHeight(temp);
     root=temp;      
}

 

2.3、右旋

/*
①讓A的右子樹成為B的左子樹
②讓B作為A的右子樹
③將根結點設為結點A
*/
void R(node* &root)
{
      node* temp=root->lchild;
      root->lchild=temp->rchild;
      temp->rchild=root;
      updateHeight(root);
      updateHeight(temp);
      root=temp;  
}

 

2.4 AVL樹結點插入

①討論結點A的平衡因子是2的情形(左子樹的高度比右子樹大2),於是以結點A為根結點的子樹一定是圖9-31的兩種形態LL型與LR型之一,

當結點A的左孩子的平衡因子是1時為LL型,是-1時為LR型。

 

現在考慮怎樣調整這兩種樹型,才能使樹平衡。

先考慮LL型,可以把以C為根結點的子樹看作一個整體,然后以結點A作為root進行右旋,便可以達到平衡,如圖9-32所示。

 

然后考慮LR型,可以先忽略結點A,以結點C為root進行左旋,再按LL型做法進行一次右旋,如圖9-33所示。

②考慮平衡因子為-2的情形(右子樹高度比左子樹大2),以結點A為根結點的子樹一定是圖9-34的兩種形態RR型與RL型之一,當結點A的右孩子的平衡因子是-1時為RR型,是1時為RL型。

 

對RR型來說,可以把以C為根結點的子樹看做一個整體,然后以結點A作為root進行左旋,便可達到平衡,如圖9-35所示。

 

對RL型來說,可以先忽略結點A,以結點C為root進行一次右旋,再按照RR型做法進行一次左旋,如圖9-36所示。

 

 AVL樹插入情況總結如下(BF表示平衡因子)

 

 現在考慮書寫插入代碼,首先,AVL樹的插入代碼是在二叉樹的插入代碼基礎上增加平衡操作的,因此,如果不考慮平衡操作,代碼是這樣的:

//插入權值為v的結點
void insert(node* &root,int v)
{
     if(root==NULL)      //到達空結點
     {
          root=newNode(v);
          return;
     }
     if(v<root->data)    //v比根結點的權值小
     {
          insert(root->lchild,v);   //往左子樹插入
     }else               //v比根結點的權值大
     {
          insert(root->rchild,v);   //往右子樹插入
     }
}

在這個基礎上,由於需要從插入的結點開始從下往上判斷結點是否失衡,因此需要在每一個insert函數之后更新當前結點的高度,並在這之后根據樹形是LL型、LR型、RR型、RL型之一來進行平衡操作。代碼如下:

//AVL樹插入代碼
//插入權值為v的結點
void insert(node* &root,int v)
{
    if(root==NULL){        //到達空結點
        root=newNode(v);
        return;
    }
    if(v<root->data){      //v比根結點權值小
        insert(root->lchild,v);  //往左子樹插入
        updateHeight(root);      //更新樹高
        if(getBalanceFactor(root)==2){
            if(getBalanceFactor(root->lchild)==1){   //LL型
                R(root);
            }else if(getBalanceFactor(root->lchild)==-1){  //LR型
                L(root->lchild);
                R(root);
            }  
        }
    }else{               //v比根結點的權值大
        insert(root->rchild,v);  //往右子樹插入
        updateHeight(root);   //更新樹高
        if(getBalanceFactor(root)==-2){
            if(getBalanceFactor(root->rchild)==-1){    //RR型
                L(root);
            }else if(getBalanceFactor(root->rchild)==1){  //RL型
                R(root->rchild);
                L(root);
            }
        }    
    }
}

 

2.5、AVL樹的建立

node* Create(int data[],int n)
{
    node* root=NULL;  //新建空根結點root
    for(int i=0;i<n;i++)
    {
        insert(root,data[i]);  //將data[0]~data[n-1]插入AVL樹中
    }
    return root;    //返回根結點
}

 

3、練習題

問題 A: 算法9-9~9-12:平衡二叉樹的基本操作

輸入

輸入的第一行包含2個正整數n和k,分別表示共有n個整數和k次查詢。其中n不超過500,k同樣不超過500。
第二行包含n個用空格隔開的正整數,表示n個整數。
第三行包含k個用空格隔開的正整數,表示k次查詢的目標。

輸出

只有1行,包含k個整數,分別表示每一次的查詢結果。如果在查詢中找到了對應的整數,則輸出1,否則輸出0。
請在每個整數后輸出一個空格,並請注意行尾輸出換行。

樣例輸入 

8 3
1 3 5 7 8 9 10 15
9 2 5

樣例輸出

1 0 1 

提示

在本題中,首先需要按照題目描述中的算法完成平衡二叉樹的構造過程,之后需要通過在平衡二叉樹中的不斷向下查找,將需要查詢的值與當前節點的值進行比較,直到確定被查詢的值是否存在。
通過課本中的性能分析部分,不難發現平衡二叉樹的查找、插入、刪除等操作的時間復雜度均為O(log2n),這是通過利用旋轉操作使二叉樹保持平衡狀態而保證的。
 
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
#define maxn 505
int data[maxn];
vector<int> vec;
//存儲結構
struct node{
    int data,height;
    node* lchild,*rchild;
};
//新建一個結點
node* newNode(int v)
{
    node* Node=new node;
    Node->data=v;
    Node->height=1;
    Node->lchild=Node->rchild=NULL;
    return Node;
}
//獲取結點root所在子樹的當前高度
int getHeight(node* root)
{
    if(root==NULL) return 0;
    return root->height;
}
//計算root結點的平衡因子
int getBalanceFactor(node* root)
{
    return getHeight(root->lchild)-getHeight(root->rchild);
}
//更新結點高度
void updateHeight(node* root)
{
    root->height=max(getHeight(root->lchild),getHeight(root->rchild))+1;
}
//查找操作
void search1(node* root,int x,vector<int> &vec)
{
    if(root==NULL){
        vec.push_back(0);
        return;
    }
    if(x==root->data){
        vec.push_back(1);
    }else if(x<root->data){
        search1(root->lchild,x,vec);
    }else{
        search1(root->rchild,x,vec);
    }
}
//左旋
void L(node* &root)
{
    node* temp=root->rchild;
    root->rchild=temp->lchild;
    temp->lchild=root;
    updateHeight(root);
    updateHeight(temp);
    root=temp;
}
//右旋
void R(node* &root)
{
    node* temp=root->lchild;
    root->lchild=temp->rchild;
    temp->rchild=root;
    updateHeight(root);
    updateHeight(temp);
    root=temp;
}
//AVL樹插入代碼
void insert1(node* &root,int v)
{
    if(root==NULL){
        root=newNode(v);  //到達空結點
        return;
    }
    if(v<root->data){
        insert1(root->lchild,v);
        updateHeight(root);  //更新樹高
        if(getBalanceFactor(root)==2){
            if(getBalanceFactor(root->lchild)==1){
                R(root);  //進行右旋
            }else if(getBalanceFactor(root->lchild)==-1){
                L(root->lchild);
                R(root);
            }
        }
    }else{
        insert1(root->rchild,v);
        updateHeight(root);
        if(getBalanceFactor(root)==-2){
            if(getBalanceFactor(root->rchild)==-1){
                L(root);
            }else if(getBalanceFactor(root->rchild)==1){
                R(root->rchild);
                L(root);
            }
        }
    }
}
//AVL樹建立
node* Create(int data[],int n)
{
    node* root=NULL;
    for(int i=0;i<n;i++)
    {
        insert1(root,data[i]);
    }
    return root;
}
int main()
{
    int n,k,id;
    while(cin>>n>>k)
    {
        node* root=new node;
        data[maxn]={0};
        for(int i=0;i<n;i++) cin>>data[i];
        root=Create(data,n);
        for(int i=0;i<k;i++){
            cin>>id;
            search1(root,id,vec);
        }
        for(int i=0;i<vec.size();i++)
        {
            if(i!=vec.size()-1) cout<<vec[i]<<" ";
            else cout<<vec[i]<<endl;
        }
        vec.clear();
    }
    return 0;
}

/*

8 7
1 3 5 7 8 9 10 15
9 12 3 7 16 8 2
1 0 1 1 0 1 0

*/

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM