AVL樹(自平衡樹)——c++實現


 
 
AVL樹是高度平衡的而二叉樹。它的特點是:AVL樹中任何節點的兩個子樹的高度最大差別為1。 
AVL樹本質上還是一棵二叉搜索樹,它的特點是:
1.本身首先是一棵二叉搜索樹。
2.帶有平衡條件:每個結點的左右子樹的高度之差的絕對值(平衡因子)最多為1。
也就是說,AVL樹,本質上是帶了平衡功能的二叉查找樹(二叉排序樹,二叉搜索樹)。
既然是樹,那么就要有節點:
template <class T>
struct AVLTreeNode{
    T data;
    int height;
    AVLTreeNode* Left;
    AVLTreeNode* Right;
    
    AVLTreeNode(T v,AVLTreeNode* l,AVLTreeNode* r):data(v),height(0),Left(l),Right(r){}
};
/*
數據解釋:
data用來儲存節點值
height儲存的是幾點的高度
Left是左兒子
Right是右兒子
最后一項是構造函數
*/
View Code

接下來我們給出AVL樹的定義

template <class T> 
class AVLTree{
    private:
                //根節點
        AVLTreeNode<T>* Root;
    public:
        AVLTree():Root(NULL){}//構造函數
        
        void add(T data);//添加節點的外部接口
        int height();//查詢高度的外部接口
        int max(int a, int b);//比較兩個數據的大小
    private:
        AVLTreeNode<T>* add(AVLTreeNode<T>* &tree, T data);//添加節點的內部接口
        int height(AVLTreeNode<T>* tree);//查詢高度的內部接口
        AVLTreeNode<T>* LL_Rotation(AVLTreeNode<T>* k2);//左左旋轉的具體實現
        AVLTreeNode<T>* RR_Rotation(AVLTreeNode<T>* k1);//右右旋轉的具體實現
        AVLTreeNode<T>* LR_Rotation(AVLTreeNode<T>* k3);//左右旋轉的具體實現
        AVLTreeNode<T>* RL_Rotation(AVLTreeNode<T>* k1);//右左旋轉的具體實現
        
};   

1.查詢高度

/*
高度
作用:獲取樹的高度 
*/
template <class T>
int AVLTree<T>::height(AVLTreeNode<T>* tree) 
{
    if (tree != NULL)
        return tree->height;

    return 0;
}

template <class T>
int AVLTree<T>::height() {
    return height(Root);
}

2.比較大小

/* 模板類改造比較兩個值的大小*/
template <class T>
int AVLTree<T>::max(int a, int b) {
    return a>b ? a : b;
}

3.旋轉

如果在AVL樹中進行插入或刪除節點后,可能導致AVL樹失去平衡。這種失去平衡的可以概括為4種姿態:LL(左左),LR(左右),RR(右右)和RL(右左)。下面給出它們的示意圖:

上圖中的4棵樹都是"失去平衡的AVL樹",從左往右的情況依次是:LL、LR、RL、RR。除了上面的情況之外,還有其它的失去平衡的AVL樹,如下圖:

上面的兩張圖都是為了便於理解,而列舉的關於"失去平衡的AVL樹"的例子。總的來說,AVL樹失去平衡時的情況一定是LL、LR、RL、RR這4種之一,它們都由各自的定義:

(1) LL:LeftLeft,也稱為"左左"。插入或刪除一個節點后,根節點的左子樹的左子樹還有非空子節點,導致"根的左子樹的高度"比"根的右子樹的高度"大2,導致AVL樹失去了平衡。
     例如,在上面LL情況中,由於"根節點(8)的左子樹(4)的左子樹(2)還有非空子節點",而"根節點(8)的右子樹(12)沒有子節點";導致"根節點(8)的左子樹(4)高度"比"根節點(8)的右子樹(12)"高2。

 

(2) LR:LeftRight,也稱為"左右"。插入或刪除一個節點后,根節點的左子樹的右子樹還有非空子節點,導致"根的左子樹的高度"比"根的右子樹的高度"大2,導致AVL樹失去了平衡。
     例如,在上面LR情況中,由於"根節點(8)的左子樹(4)的左子樹(6)還有非空子節點",而"根節點(8)的右子樹(12)沒有子節點";導致"根節點(8)的左子樹(4)高度"比"根節點(8)的右子樹(12)"高2。

 

(3) RL:RightLeft,稱為"右左"。插入或刪除一個節點后,根節點的右子樹的左子樹還有非空子節點,導致"根的右子樹的高度"比"根的左子樹的高度"大2,導致AVL樹失去了平衡。
     例如,在上面RL情況中,由於"根節點(8)的右子樹(12)的左子樹(10)還有非空子節點",而"根節點(8)的左子樹(4)沒有子節點";導致"根節點(8)的右子樹(12)高度"比"根節點(8)的左子樹(4)"高2。

 

(4) RR:RightRight,稱為"右右"。插入或刪除一個節點后,根節點的右子樹的右子樹還有非空子節點,導致"根的右子樹的高度"比"根的左子樹的高度"大2,導致AVL樹失去了平衡。
     例如,在上面RR情況中,由於"根節點(8)的右子樹(12)的右子樹(14)還有非空子節點",而"根節點(8)的左子樹(4)沒有子節點";導致"根節點(8)的右子樹(12)高度"比"根節點(8)的左子樹(4)"高2。

 

前面說過,如果在AVL樹中進行插入或刪除節點后,可能導致AVL樹失去平衡。AVL失去平衡之后,可以通過旋轉使其恢復平衡,下面分別介紹"LL(左左),LR(左右),RR(右右)和RL(右左)"這4種情況對應的旋轉方法。

3.1LL旋轉

/*
LL
在左左旋轉中,一共涉及到三代節點,我們把爺爺節點命名為K2,K2的左兒子命名為K1。
問題出現的原因是K1的左兒子增加了一個節點導致平衡樹失衡
解決思路:
    讓K1成為爺爺節點,K2成為K1的右兒子,並且將K1的右兒子接為K2的左兒子,然后返回爺爺節點K1取代原來K2的位置 
*/ 
template <class T>
AVLTreeNode<T>* AVLTree<T>::LL_Rotation(AVLTreeNode<T>* k2){
    AVLTreeNode<T>* k1;

    k1 = k2->Left;
    k2->Left = k1->Right;
    k1->Right = k2;

    k2->height = max( height(k2->Left), height(k2->Right)) + 1;
    k1->height = max( height(k1->Left), k2->height) + 1;

    return k1;
}

3.2RR旋轉

/*
RR
在右右旋轉中,一共涉及到三代節點,我們把爺爺節點命名為K1,K1的右兒子命名為K2。
問題出現的原因是K2的右兒子增加了一個節點導致平衡樹失衡
解決思路:
    讓K2成為爺爺節點,K1成為K2的左兒子,並且將K2的左兒子接為K1的右兒子,然后返回爺爺節點K2取代原來K1的位置 
*/ 
template <class T>
AVLTreeNode<T>* AVLTree<T>::RR_Rotation(AVLTreeNode<T>* k1){
    AVLTreeNode<T>* k2;

    k2 = k1->Right;
    k1->Right = k2->Left;
    k2->Left = k1;

    k1->height = max( height(k1->Left), height(k1->Right)) + 1;
    k2->height = max( height(k2->Right), k1->height) + 1;

    return k2;
}

3.3LR旋轉

 

/*
LR
在左右旋轉中,一共涉及到四代節點,我們把做根本的節點成為K3(曾爺爺節點),K3的左兒子稱為K1(爺爺節點),K1的右兒子稱為K2
問題出現的原因時K2的右兒子增加了一個節點之后導致樹的失衡
解決思路:
    因為涉及到四代節點,所以需要兩次旋轉,
    首先對K1,K2進行一次右右旋轉 =》 K2成為爺爺節點(即K3的左兒子),k2原本的左兒子稱為K1的右兒子,K1成為K2的左兒子 
    接下來對K2,K3進行一次左左旋轉 =》K2稱為曾爺爺節點,K2原本的右兒子成為K3的左兒子,K3成為K2的右兒子  
*/
template <class T>
AVLTreeNode<T>* AVLTree<T>::LR_Rotation(AVLTreeNode<T>* k3){
    k3->Left = RR_Rotation(k3->Left);

    return LL_Rotation(k3);
}

3.4RL旋轉

/*
RL
在右左旋轉中,一共涉及到四代節點,我們把做根本的節點成為K1(曾爺爺節點),K1的右兒子稱為K3(爺爺節點),K3的左兒子稱為K2
問題出現的原因時K2的左兒子增加了一個節點之后導致樹的失衡
解決思路:
    因為涉及到四代節點,所以需要兩次旋轉,
    首先對K2,K3進行一次左左旋轉 =》 K2成為爺爺節點(即K1的右兒子),k2原本的右兒子稱為K3的左兒子,K3成為K2的右兒子 
    接下來對K1,K2進行一次右右旋轉 =》K2稱為曾爺爺節點,K2原本的左兒子成為K1的右兒子,K1成為K2的左兒子  
*/
template <class T>
AVLTreeNode<T>* AVLTree<T>::RL_Rotation(AVLTreeNode<T>* k1){
    k1->Right = LL_Rotation(k1->Right);

    return RR_Rotation(k1);
}

 4.插入節點

template <class T>
AVLTreeNode<T>* AVLTree<T>::add(AVLTreeNode<T>* &tree, T data){
    if (tree == NULL) {
        tree = new AVLTreeNode<T>(data, NULL, NULL);
    }
    else if (data < tree->data){ 
        //將新加入的節點插入左子樹 
        tree->Left = add(tree->Left, data);
        //檢查加入新的結點之后樹是否失去平衡 
        if (height(tree->Left) - height(tree->Right) == 2)
        {
            if (data < tree->Left->data)
                tree = LL_Rotation(tree);//左左,新加入之后左兒子的左兒子深了  
            else
                tree = LR_Rotation(tree);//左右,新加入之后左兒子的右兒子深了
        }
    }
    //將新加入的節點插入右子樹 
    else if (data > tree->data) {
        tree->Right = add(tree->Right, data);
        //檢查加入新的結點之后樹是否失去平衡 
        if (height(tree->Right) - height(tree->Left) == 2)
        {
            if (data > tree->Right->data)
                tree = RR_Rotation(tree);//右右,新加入之后右兒子的右兒子深了  
            else
                tree = RL_Rotation(tree);//右左,新加入之后右兒子的左兒子深了  
        }
    }
    else //該節點已經在樹中 
    {
        cout << "該節點已經存在樹中" << endl;
    }
    //更新更前當前節點的高度 
    tree->height = max( height(tree->Left), height(tree->Right)) + 1;

    return tree;
}

template <class T>
void AVLTree<T>::add(T data){
    add(Root, data);
}

總的代碼:

#include <cstdio>
#include <iostream>

using namespace std;

template <class T>
struct AVLTreeNode{
    T data;
    int height;
    AVLTreeNode* Left;
    AVLTreeNode* Right;
    
    AVLTreeNode(T v,AVLTreeNode* l,AVLTreeNode* r):data(v),height(0),Left(l),Right(r){}
};
/*
AVL樹的定義
為了保護類內數據,仿照網絡實例把函數寫成了內接口和外接口的形式。還有模板類。 
感覺代碼有點繁雜,寫完之后調式的時候感覺不太順手,以后寫程序要注意內接口和外接口的模式 
*/
template <class T> 
class AVLTree{
    private:
        AVLTreeNode<T>* Root;
    public:
        AVLTree():Root(NULL){}
        
        void add(T data);
        int height();
        int max(int a, int b);
    private:
        AVLTreeNode<T>* add(AVLTreeNode<T>* &tree, T data);
        int height(AVLTreeNode<T>* tree);
        AVLTreeNode<T>* LL_Rotation(AVLTreeNode<T>* k2);
        AVLTreeNode<T>* RR_Rotation(AVLTreeNode<T>* k1);
        AVLTreeNode<T>* LR_Rotation(AVLTreeNode<T>* k3);
        AVLTreeNode<T>* RL_Rotation(AVLTreeNode<T>* k1);
        
};
/*
高度
作用:獲取樹的高度 
*/
template <class T>
int AVLTree<T>::height(AVLTreeNode<T>* tree) 
{
    if (tree != NULL)
        return tree->height;

    return 0;
}

template <class T>
int AVLTree<T>::height() {
    return height(Root);
}
/* 模板類改造比較兩個值的大小*/
template <class T>
int AVLTree<T>::max(int a, int b) {
    return a>b ? a : b;
}

/*
LL
在左左旋轉中,一共涉及到三代節點,我們把爺爺節點命名為K2,K2的左兒子命名為K1。
問題出現的原因是K1的左兒子增加了一個節點導致平衡樹失衡
解決思路:
    讓K1成為爺爺節點,K2成為K1的右兒子,並且將K1的右兒子接為K2的左兒子,然后返回爺爺節點K1取代原來K2的位置 
*/ 
template <class T>
AVLTreeNode<T>* AVLTree<T>::LL_Rotation(AVLTreeNode<T>* k2){
    AVLTreeNode<T>* k1;

    k1 = k2->Left;
    k2->Left = k1->Right;
    k1->Right = k2;

    k2->height = max( height(k2->Left), height(k2->Right)) + 1;
    k1->height = max( height(k1->Left), k2->height) + 1;

    return k1;
}
/*
RR
在右右旋轉中,一共涉及到三代節點,我們把爺爺節點命名為K1,K1的右兒子命名為K2。
問題出現的原因是K2的右兒子增加了一個節點導致平衡樹失衡
解決思路:
    讓K2成為爺爺節點,K1成為K2的左兒子,並且將K2的左兒子接為K1的右兒子,然后返回爺爺節點K2取代原來K1的位置 
*/ 
template <class T>
AVLTreeNode<T>* AVLTree<T>::RR_Rotation(AVLTreeNode<T>* k1){
    AVLTreeNode<T>* k2;

    k2 = k1->Right;
    k1->Right = k2->Left;
    k2->Left = k1;

    k1->height = max( height(k1->Left), height(k1->Right)) + 1;
    k2->height = max( height(k2->Right), k1->height) + 1;

    return k2;
}
/*
LR
在左右旋轉中,一共涉及到四代節點,我們把做根本的節點成為K3(曾爺爺節點),K3的左兒子稱為K1(爺爺節點),K1的右兒子稱為K2
問題出現的原因時K2的右兒子增加了一個節點之后導致樹的失衡
解決思路:
    因為涉及到四代節點,所以需要兩次旋轉,
    首先對K1,K2進行一次右右旋轉 =》 K2成為爺爺節點(即K3的左兒子),k2原本的左兒子稱為K1的右兒子,K1成為K2的左兒子 
    接下來對K2,K3進行一次左左旋轉 =》K2稱為曾爺爺節點,K2原本的右兒子成為K3的左兒子,K3成為K2的右兒子  
*/
template <class T>
AVLTreeNode<T>* AVLTree<T>::LR_Rotation(AVLTreeNode<T>* k3){
    k3->Left = RR_Rotation(k3->Left);

    return LL_Rotation(k3);
}
/*
RL
在右左旋轉中,一共涉及到四代節點,我們把做根本的節點成為K1(曾爺爺節點),K1的右兒子稱為K3(爺爺節點),K3的左兒子稱為K2
問題出現的原因時K2的左兒子增加了一個節點之后導致樹的失衡
解決思路:
    因為涉及到四代節點,所以需要兩次旋轉,
    首先對K2,K3進行一次左左旋轉 =》 K2成為爺爺節點(即K1的右兒子),k2原本的右兒子稱為K3的左兒子,K3成為K2的右兒子 
    接下來對K1,K2進行一次右右旋轉 =》K2稱為曾爺爺節點,K2原本的左兒子成為K1的右兒子,K1成為K2的左兒子  
*/
template <class T>
AVLTreeNode<T>* AVLTree<T>::RL_Rotation(AVLTreeNode<T>* k1){
    k1->Right = LL_Rotation(k1->Right);

    return RR_Rotation(k1);
}

template <class T>
AVLTreeNode<T>* AVLTree<T>::add(AVLTreeNode<T>* &tree, T data){
    if (tree == NULL) {
        tree = new AVLTreeNode<T>(data, NULL, NULL);
    }
    else if (data < tree->data){ 
        //將新加入的節點插入左子樹 
        tree->Left = add(tree->Left, data);
        //檢查加入新的結點之后樹是否失去平衡 
        if (height(tree->Left) - height(tree->Right) == 2)
        {
            if (data < tree->Left->data)
                tree = LL_Rotation(tree);//左左,新加入之后左兒子的左兒子深了  
            else
                tree = LR_Rotation(tree);//左右,新加入之后左兒子的右兒子深了
        }
    }
    //將新加入的節點插入右子樹 
    else if (data > tree->data) {
        tree->Right = add(tree->Right, data);
        //檢查加入新的結點之后樹是否失去平衡 
        if (height(tree->Right) - height(tree->Left) == 2)
        {
            if (data > tree->Right->data)
                tree = RR_Rotation(tree);//右右,新加入之后右兒子的右兒子深了  
            else
                tree = RL_Rotation(tree);//右左,新加入之后右兒子的左兒子深了  
        }
    }
    else //該節點已經在樹中 
    {
        cout << "該節點已經存在樹中" << endl;
    }
    //更新更前當前節點的高度 
    tree->height = max( height(tree->Left), height(tree->Right)) + 1;

    return tree;
}

template <class T>
void AVLTree<T>::add(T data){
    add(Root, data);
}

int main(){
    int num;
    AVLTree<int>* tree=new AVLTree<int>();
    cin>>num;
    for(int i=0;i<num;i++){
        int x;
        cin>>x;
        tree->add(x);
    } 
    cout<<"高度為:"<<tree->height()<<endl; 
    return 0;
}
/*
實例輸入:
16
3 2 1 4 5 6 7 16 15 14 13 12 11 10 8 9
實例輸出:
5 
*/
View Code

 源自:http://www.cnblogs.com/skywang12345/p/3577360.html


免責聲明!

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



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