AVL樹—— C++實現

AVL樹的介紹


二叉查找樹的深度越小,那麼查找所需要的運算時間越小。一個深度爲log(n)的二叉查找樹,查找算法的時間複雜度也是log(n)。然而,我們在二叉查找樹中已經實現的插入和刪除操作並不能讓保持log(n)的深度。如果我們按照8,7,6,5,4,3,2,1的順序插入節點,那麼就是一個深度爲n的二叉樹,即二叉查找樹退化成單向鏈表。那麼,查找算法的時間複雜度爲n。

AVL樹是高度平衡的查找二叉樹。它的特點是:AVL樹中任何節點的兩個子樹的最大高度差爲1,這樣的平衡特性能保證查找、插入、刪除三大操作的平均或最壞情況下的算法複雜度維持在log(n)。
這裏寫圖片描述

上面的兩張圖片,左邊的是AVL樹,它的任何節點的兩個子樹的高度差別都<=1;而右邊的不是AVL樹,因爲7的兩顆子樹的高度相差爲2(以2爲根節點的樹的高度是3,而以8爲根節點的樹的高度是1)。

AVL樹的C++實現


1. AVL樹節點


#pragma once
/*
***AVLTreeNode: AVLTree的節點
***key: 關鍵字,節點根據關鍵字排序,用來搜索
***height: 以節點在根的子樹的高度
***leftChild: 節點的左孩子
***rightChild: 節點的右孩子
*/

template <typename T>
struct AVLTreeNode
{
    T key;
    int height;
    AVLTreeNode<T> *leftChild,
        *rightChild;

    AVLTreeNode(const T &theKey) : key(theKey)
    {
        height = 0;
        leftChild = rightChild = nullptr;
    }
    AVLTreeNode(const T &theKey, AVLTreeNode<T>* leftChild,
        AVLTreeNode<T>* rightChild) : key(theKey)
    {
        height = 0;
        this->leftChild = leftChild;
        this->rightChild = rightChild;;
    }

};

2. AVL樹接口


template <typename T>
class AVLTree
{
public:

    AVLTree();
    ~AVLTree();

    // 獲取AVL樹的高度
    int height();

    // 前序、中序、後序遍歷"AVL樹"
    void preOrder() { preOrder(root); }
    void inOrder() { inOrder(root); }
    void postOrder() { postOrder(root); }

    // 查找"AVL樹"中鍵值爲key的節點
    AVLTreeNode<T>* find(const T& key) const;

    // 查找最小和最大結點:返回結點的鍵值指針。
    T* minimum();
    T* maximum();

    // 將結點插入到AVL樹中
    void insert(const T& theKey);

    // 刪除結點
    void erase(const T& theKey);

    // 銷燬AVL樹
    void destroy();

    void output() { inOrder(root); cout << endl; }

private:

    AVLTreeNode<T> *root;    // 根結點

    int height(AVLTreeNode<T>* theRoot);

    // 前序、中序、後序遍歷"AVL樹"
    void preOrder(AVLTreeNode<T>* theRoot) const;
    void inOrder(AVLTreeNode<T>* theRoot) const;
    void postOrder(AVLTreeNode<T>* theRoot) const;

    // 查找最小和最大結點,返回節點指針。
    AVLTreeNode<T>* minimum(AVLTreeNode<T>* theRoot) const;
    AVLTreeNode<T>* maximum(AVLTreeNode<T>* theRoot) const;

    // LL:左左對應的情況(左單旋轉)AVLTreeNode<T>* LLRotation(AVLTreeNode<T>* aNode);

    // RR:右右對應的情況(右單旋轉)AVLTreeNode<T>* RRRotation(AVLTreeNode<T>* aNode);

    // LR:左右對應的情況(左雙旋轉)AVLTreeNode<T>* LRRotation(AVLTreeNode<T>* aNode);

    // RL:右左對應的情況(右雙旋轉)AVLTreeNode<T>* RLRotation(AVLTreeNode<T>* aNode);

    // 將結點(z)插入到AVL樹中
    AVLTreeNode<T>* insert(AVLTreeNode<T>* &theRoot, const T& theKey);

    // 刪除AVL樹中的結點(p),並返回被刪除的結點
    AVLTreeNode<T>* erase(AVLTreeNode<T>* &theRoot, AVLTreeNode<T>* p);

    // 銷燬AVL樹
    void destroy(AVLTreeNode<T>* &theRoot);

};

AVLTree是AVL樹對應的類。它包含AVL樹的根節點root和AVL樹的基本操作接口。需要說明的是:AVLTree中重載了許多函數。重載的目的是區分內部接口和外部接口,內部接口主要用於外部接口的遞歸實現,而外部接口供用戶使用;例如insert()函數而言,insert(root, theKey)是內部接口,而insert(theKey)是外部接口。

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種情況對應的旋轉方法。

LL的旋轉


LL失去平衡的情況,可以通過一次旋轉讓AVL樹恢復平衡。如下圖:

這裏寫圖片描述

圖中左邊是旋轉之前的樹,右邊是旋轉之後的樹。從中可以發現,旋轉之後的樹又變成了AVL樹,而且該旋轉只需要一次即可完成。
對於LL旋轉,你可以這樣理解爲:LL旋轉是圍繞”失去平衡的AVL根節點”進行的,也就是節點B;而且由於是LL情況,即左左情況,使A繞着B順時針旋轉。將B變成根節點,A變成B的右子樹,”B的右子樹”變成”A的左子樹”。

LLRotation代碼:

// LL型:左左對應的情況(左單旋轉)
template <typename T>
AVLTreeNode<T>* AVLTree<T>::LLRotation(AVLTreeNode<T>* aNode)
{
    AVLTreeNode<T>* bNode = aNode->leftChild;
    aNode->leftChild = bNode->rightChild;
    bNode->rightChild = aNode;

    aNode->height = max(height(aNode->leftChild), height(aNode->rightChild)) + 1;
    bNode->height = max(height(bNode->leftChild), height(bNode->rightChild)) + 1;

    return bNode;
}

RR的旋轉


理解了LL之後,RR就相當容易理解了。RR是與LL對稱的情況!RR恢復平衡的旋轉方法如下:

這裏寫圖片描述

RR情況下,A節點繞着B節點逆時針旋轉,使得A成爲B的左孩子,B的左孩子成爲A的右孩子。圖中左邊是旋轉之前的樹,右邊是旋轉之後的樹。RR旋轉也只需要一次即可完成。

RRRotation代碼:

// RR型:右右對應的情況(右單旋轉)。
template <typename T>
AVLTreeNode<T>* AVLTree<T>::RRRotation(AVLTreeNode<T>* aNode)
{
    AVLTreeNode<T>* bNode = aNode->rightChild;
    aNode->rightChild = bNode->leftChild;
    bNode->leftChild = aNode;

    aNode->height = max(height(aNode->leftChild), height(aNode->rightChild)) + 1;
    bNode->height = max(height(bNode->leftChild), height(bNode->rightChild)) + 1;

    return bNode;
}

LR的旋轉


LR失去平衡的情況,需要經過兩次旋轉才能讓AVL樹恢復平衡。如下圖:

這裏寫圖片描述

第一次旋轉是圍繞”B”進行的”RR旋轉”,第二次是圍繞”C”進行的”LL旋轉”。

LRRotation代碼:

// LR型:左右對應的情況(左雙旋轉)。
template <typename T>
AVLTreeNode<T>* AVLTree<T>::LRRotation(AVLTreeNode<T>* aNode)
{
    aNode->leftChild = RRRotation(aNode->leftChild);    
    return LLRotation(aNode);
}

RL的旋轉


RL是與LR的對稱情況!RL恢復平衡的旋轉方法如下:

這裏寫圖片描述

第一次旋轉是圍繞”B”進行的”LL旋轉”,第二次是圍繞”C”進行的”RR旋轉”。

RLRotation代碼:

// RL型:右左對應的情況(右雙旋轉)。
template <typename T>
AVLTreeNode<T>* AVLTree<T>::RLRotation(AVLTreeNode<T>* aNode)
{
    aNode->rightChild = LLRotation(aNode->rightChild);
    return RRRotation(aNode);
}

3. 完整的實現代碼


AVL樹的實現文件(AVRTree.h)

#pragma once
#include<iostream>
#include<algorithm>
#include"AVLTreeNode.h"

using namespace std;

template <typename T>
class AVLTree

{
public:

    AVLTree() : root(nullptr) { }
    ~AVLTree() { destroy(); }

    // 獲取AVL樹的高度
    int height();

    // 前序、中序、後序遍歷"AVL樹"
    void preOrder() { preOrder(root); }
    void inOrder() { inOrder(root); }
    void postOrder() { postOrder(root); }

    // 查找"AVL樹"中鍵值爲key的節點
    AVLTreeNode<T>* find(const T& key) const;

    // 查找最小和最大結點:返回結點的鍵值指針。
    T* minimum();
    T* maximum();

    // 將結點插入到AVL樹中
    void insert(const T& theKey);

    // 刪除結點
    void erase(const T& theKey);

    // 銷燬AVL樹
    void destroy();

    void output() { inOrder(root); cout << endl; }

private:

    AVLTreeNode<T> *root;    // 根結點

    int height(AVLTreeNode<T>* theRoot);

    // 前序、中序、後序遍歷"AVL樹"
    void preOrder(AVLTreeNode<T>* theRoot) const;
    void inOrder(AVLTreeNode<T>* theRoot) const;
    void postOrder(AVLTreeNode<T>* theRoot) const;

    // 查找最小和最大結點,返回節點指針。
    AVLTreeNode<T>* minimum(AVLTreeNode<T>* theRoot) const;
    AVLTreeNode<T>* maximum(AVLTreeNode<T>* theRoot) const;

    // LL:左左對應的情況(左單旋轉)。
    AVLTreeNode<T>* LLRotation(AVLTreeNode<T>* aNode);

    // RR:右右對應的情況(右單旋轉)。
    AVLTreeNode<T>* RRRotation(AVLTreeNode<T>* aNode);

    // LR:左右對應的情況(左雙旋轉)。
    AVLTreeNode<T>* LRRotation(AVLTreeNode<T>* aNode);

    // RL:右左對應的情況(右雙旋轉)。
    AVLTreeNode<T>* RLRotation(AVLTreeNode<T>* aNode);

    // 將結點(z)插入到AVL樹中
    AVLTreeNode<T>* insert(AVLTreeNode<T>* &theRoot, const T& theKey);

    // 刪除AVL樹中的結點(p),並返回被刪除的結點
    AVLTreeNode<T>* erase(AVLTreeNode<T>* &theRoot, AVLTreeNode<T>* p);

    // 銷燬AVL樹
    void destroy(AVLTreeNode<T>* &theRoot);

};

template <typename T>
int AVLTree<T>::height(AVLTreeNode<T>* theRoot)
{
    if(theRoot != nullptr)
        return theRoot->height;
    return 0;
}

template <typename T>
int AVLTree<T>::height()
{
    return height(root);
}

template <typename T>
void AVLTree<T>::preOrder(AVLTreeNode<T>* theRoot) const
{
    if (theRoot != nullptr)
    {
        cout << theRoot->key << " ";
        preOrder(theRoot->leftChild);
        preOrder(theRoot->rightChild);
    }
}
template <typename T>
void AVLTree<T>::inOrder(AVLTreeNode<T>* theRoot) const
{
    if (theRoot != nullptr)
    {
        inOrder(theRoot->leftChild);
        cout << theRoot->key << " ";
        inOrder(theRoot->rightChild);
    }
}
template <typename T>
void AVLTree<T>::postOrder(AVLTreeNode<T>* theRoot) const
{
    if (theRoot != nullptr)
    {
        postOrder(theRoot->leftChild);
        postOrder(theRoot->rightChild);
        cout << theRoot->key << " ";
    }
}

template <typename T>
AVLTreeNode<T>* AVLTree<T>::find(const T& theKey) const
{
    AVLTreeNode<T>* p = root;
    while (p != nullptr)
    {
        if (theKey < p->key)
            p = p->leftChild;
        else if (theKey > p->key)
            p = p->rightChild;
        else
            return p;
    }

    return nullptr;
}

template <typename T>
T* AVLTree<T>::minimum()
{
    AVLTreeNode<T>* min = minimum(root);
    if (min != nullptr)
        return &min->key;
    return nullptr;
}
template <typename T>
AVLTreeNode<T>* AVLTree<T>::minimum(AVLTreeNode<T>* theRoot) const
{
    AVLTreeNode<T>* p = theRoot,
        pp = nullptr;

    while (p != nullptr)
    {
        pp = p;
        p = p->leftChild;
    }

    return pp;
}

template <typename T>
T* AVLTree<T>::maximum()
{
    AVLTreeNode<T>* max = maximum(root);
    if (max != nullptr)
        return &max->key;
    return nullptr;
}
template <typename T>
AVLTreeNode<T>* AVLTree<T>::maximum(AVLTreeNode<T>* theRoot) const
{
    AVLTreeNode<T>* p = theRoot,
        pp = nullptr;

    while (p != nullptr)
    {
        pp = p;
        p = p->rightChild;
    }

    return pp;
}

// LL型:左左對應的情況(左單旋轉)
template <typename T>
AVLTreeNode<T>* AVLTree<T>::LLRotation(AVLTreeNode<T>* aNode)
{
    AVLTreeNode<T>* bNode = aNode->leftChild;
    aNode->leftChild = bNode->rightChild;
    bNode->rightChild = aNode;

    aNode->height = max(height(aNode->leftChild), height(aNode->rightChild)) + 1;
    bNode->height = max(height(bNode->leftChild), height(bNode->rightChild)) + 1;

    return bNode;
}

// RR型:右右對應的情況(右單旋轉)。
template <typename T>
AVLTreeNode<T>* AVLTree<T>::RRRotation(AVLTreeNode<T>* aNode)
{
    AVLTreeNode<T>* bNode = aNode->rightChild;
    aNode->rightChild = bNode->leftChild;
    bNode->leftChild = aNode;

    aNode->height = max(height(aNode->leftChild), height(aNode->rightChild)) + 1;
    bNode->height = max(height(bNode->leftChild), height(bNode->rightChild)) + 1;

    return bNode;
}

// LR型:左右對應的情況(左雙旋轉)。
template <typename T>
AVLTreeNode<T>* AVLTree<T>::LRRotation(AVLTreeNode<T>* aNode)
{
    aNode->leftChild = RRRotation(aNode->leftChild);    
    return LLRotation(aNode);
}

// RL型:右左對應的情況(右雙旋轉)。
template <typename T>
AVLTreeNode<T>* AVLTree<T>::RLRotation(AVLTreeNode<T>* aNode)
{
    aNode->rightChild = LLRotation(aNode->rightChild);
    return RRRotation(aNode);
}


template <typename T>
void AVLTree<T>::insert(const T& theKey)
{
    insert(root, theKey);
}
//用遞歸將theKey插入到AVL樹中,並維持樹的平衡
template <typename T>
AVLTreeNode<T>* AVLTree<T>::insert(AVLTreeNode<T>* &theRoot, const T& theKey)
{
    if (theRoot == nullptr)
    {
        theRoot = new AVLTreeNode<T>(theKey);
        if (theRoot == nullptr)
        {
            cerr << "ERROR: create avltree node failed!" << endl;
            return nullptr;
        }
    }

    else if (theKey < theRoot->key)
    {//左子樹一側插入節點
        theRoot->leftChild = insert(theRoot->leftChild, theKey);
        if ( height(theRoot->leftChild) - height(theRoot->rightChild) == 2)
        { //插入節點後,AVL樹失去平衡,平衡因子爲2
            if (theKey < theRoot->leftChild->key)  //LL型
                theRoot = LLRotation(theRoot);
            else                             //LR型
                theRoot = LRRotation(theRoot);
        }
    }

    else if (theKey > theRoot->key)
    { //右子樹一側插入節點
        theRoot->rightChild = insert(theRoot->rightChild, theKey);
        if (height(theRoot->rightChild) - height(theRoot->leftChild) == 2)
        { //插入節點後,AVL樹失去平衡,平衡因子爲-2
            if (theKey > theRoot->rightChild->key)  //RR型
                theRoot = RRRotation(theRoot);
            else                              //RL型
                theRoot = RLRotation(theRoot);
        }
    }

    else   //theKey == tree->key
        cerr << "添加失敗:不允許添加相同的節點!" << endl;

    theRoot->height = max(height(theRoot->leftChild), height(theRoot->rightChild));

    return theRoot;
}


template <typename T>
void AVLTree<T>::erase(const T& theKey)
{
    AVLTreeNode<T>* P = find(theKey);  //找到theKey對應的節點
    if(p != nullptr)
        root = erase(root, p);
}
template <typename T>
AVLTreeNode<T>* AVLTree<T>::erase(AVLTreeNode<T>* &theRoot, AVLTreeNode<T>* p)
{
    if (theRoot == nullptr || p == nullptr)
        return nullptr;

    if (p->key < theRoot->key)
    { //待刪除節點在左子樹一側
        theRoot->leftChild = erase(theRoot->leftChild, p);
        if (height(theRoot->right) - height(theRoot->leftChild) = 2)
        { //刪除節點後,AVL樹失去平衡,平衡因子爲-2
            AVLTreeNode<T>* r = theRoot->rightChild;
            if (height(r->leftChild) > height(r->rightChild))
                theRoot = RLRotation(theRoot);
            else
                theRoot = RRRotation(theRoot);
        }
    }

    else if (p->key > theRoot->key)
    { //待刪除節點在右子樹一側
        theRoot->rightChild = erase(theRoot->rightChild, p);
        if (height(theRoot->leftChild) - height(theRoot->rightChild) == 2)
        { //刪除節點後,AVL樹失去平衡,平衡因子爲2
            AVLTreeNode<T>* l = theRoot->leftChild;
            if (l->rightChild > l->leftChild)
                theRoot = LRRotation(theRoot);
            else
                theRoot = LLRotation(theRoot);
        }
    }

    else
    { //待刪除的節點就是theRoot
        if ((theRoot->leftChild != nullptr) && (theRoot->rightChild != nullptr))
        { //待刪除節點有兩個孩子
            if (height(theRoot->leftChild) > height(theRoot->rightChild))
            { //從theRoot的左子樹找到最大節點填補到刪除的節點位置
                AVLTreeNode<T>* max = maximum(theRoot->leftChild);
                theRoot->key = max->key;
                theRoot->leftChild = erase(theRoot->leftChild, max);
            }
            else
            {//從theRoot的左子樹找到最大節點填補到刪除的節點位置
                AVLTreeNode<T>* min = minimum(theRoot->rightChild);
                theRoot->key = min->key;
                theRoot->rightChild = erase(theRoot->rightChild, min);
            }
        }
        else
        { //待刪除的節點最多有一個孩子
            AVLTreeNode<T>* tmp = theRoot;
            theRoot = (theRoot->leftChild != nullptr) ? theRoot->leftChild : theRoot->rightChild;
            delete tmp;
        }
    }

    return theRoot;
}

template <typename T>
void AVLTree<T>::destroy(AVLTreeNode<T>* &theRoot)
{
    if (theRoot == nullptr)
        return;

    destroy(theRoot->leftChild);
    destroy(theRoot->rightChild);

    delete theRoot;
    theRoot = nullptr;
}

template <typename T>
void AVLTree<T>::destroy()
{
    destroy(root);
}

博客轉載自:http://www.cnblogs.com/skywang12345/p/3577360.html

發佈了30 篇原創文章 · 獲贊 22 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章