平衡二叉樹仍然是一棵二叉查找樹,只是在其基礎上增加了“平衡”要求
平衡是指:對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:平衡二叉樹的基本操作
輸入
輸出
提示
#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
*/