樹的平衡 AVL Tree


本篇隨筆主要從以下三個方面介紹樹的平衡:

1):BST不平衡問題

2):BST 旋轉

3):AVL Tree

一:BST不平衡問題的解析

之前有提過普通BST的一些一些缺點,例如BST的高度是介於lgN和N之間的,如果是N的的話,顯然效率很低,不是我們需要的;但是在實際情況中,BST的高度h = N的情況卻經常出現,例如下圖所示。在BST中search,insert的running time都等於BST的高度h,我們肯定希望高度h越小越好,best case就是lgN。下圖的example 2的情況,我們會稱之為這個BST是不平衡的。 所以如果遇到這種不平衡的BST,我們如何解決呢?如何將不平衡的BST轉化成平衡的BST呢?如何將BST的高度h從N轉化成lgN呢?

 

二:樹的平衡

下面我們就來介紹一下樹的旋轉 rotation。BST是可以經過一些旋轉操作后,仍然保持BST的結構不變的,即對於每一個node,該node的left child的值總是小於這個node的值,而該node的right child的值總是大於這個node的值。經過總結,這個旋轉主要可以分為4中模式,這四種模式如下面的兩圖所示:

這四種rotation的方式是由BST的特性所決定的,至於為什么這樣旋轉是是正確的,也是由BST的特點所確定的,我在這就不證明了。只要大家記住BST可以有這4中旋轉模式即可。其實上面所示的rotation過程,都是為了平衡樹的目的。 那么現在有一個問題來了,我們說了這么多平衡,不平衡的概念,前面我們都是通過直觀的感受來體味平衡或者不平衡,那么到底有什么明確的指標可以指明一個BST到底是平衡還是不平衡的呢???這個指標到底是什么呢?那么下面就要解釋AVL Tree的概念了。

三:AVL Tree

首先AVL Tree要滿足以下2個條件:

1. AVL Tree遵循BST的結構;即left child 小於當前node, right child大於當前node。

2.每一個node的2個 child nodes的高度相差不大於1。

根據上面的條件,我們可以看出AVL Tree其本質是一種特殊的BST。所以我們現在有一個定性的指標來判斷一個BST是不是平衡的了,這個指標就是上面2個條件。當然了BST中有很多指標來判讀一個BST是不是平衡的,我們這里只是用AVL Tree作為其中之一個指標,你也可以用其他的指標方法。

所以AVL Tree是平衡的,其高度是h=lgN;

在AVL Tree中,每一個node的高度等於取其2個child node 的較大的高度值加1,即max[left child height, right child height]+1; 若node==NULL,則其高度默認為-1.

當在構建AVL Tree的過程中,向其中insert node的時候,首先第一步跟BST insert一樣,然后第二步是要檢查insert后node的2個children之間的高度差,然后根據相應的高度差來判斷相應的rotation的pattern,經過旋轉后,使整個Tree仍然保持AVL Tree的特性,即滿足上面的2個條件,所以仍然是平衡的。由於insert,search的操作的時間復雜度在BST中都是等於樹的高度,AVL Tree作為一種特殊的BST,insert, search的操作的時間復雜度自然也是等於AVL的高度h=lgN. 這樣的時間復雜度還是可以讓我們滿意的,其效率也要遠遠高於O(N)。AVL Tree的C++ 實現過程如下面的代碼所示,以下代碼實現了AVL Tree的insertion, sorting, rotation等功能。代碼僅供學習交流等非盈利使用,不能用於商業目的,作者保留追溯的權利。

#include "AVLTree.hpp"
using namespace std;

AVL_Tree::AVL_Tree(){
    
    this->root = NULL;
}

void AVL_Tree::setRoot(Node *root){
    
    this->root = root;
}
Node *AVL_Tree::getRoot(){
    
    return this->root;
}

/*
 * height of tree or subtree
 *
 * a node's height equals the max of node's left child's height and node's right child's height plus 1
 *
 *parameters:
            1, node;//the node that we want to measure with
 *
 *return: the height of the node
 */
int AVL_Tree::height(Node *node){
    
    int h = -1;
    
    if (node != NULL) {
        
        int l_height = height(node->getLeft());
        int r_height = height(node->getRight());
        h = std::max(l_height,r_height) + 1;
    }
    
    return h;
}
/*
 * the height difference of two children nodes
 *
 *parameters:
 *          1, node;//the node which we want to know the differences of its two children
 *
 *return: int; the height difference of the two children nodes
 */

int AVL_Tree::heightDiff(Node *node){
    
    int l_height = height(node->getLeft());
    int r_height = height(node->getRight());
    
    return l_height-r_height;
    
}


/*
 *
 *4 types of rotations
 *
 *1)left left pattern
 *2)left right pattern
 *3)right right pattern
 *4)right left pattern
 *
 */
void AVL_Tree::ll_rotation(Node *node){

    int value = node->getData();
    Node *temp = node->getLeft();
    
    node->setData(temp->getData());
    node->setLeft(temp->getLeft());
    
    temp->setData(value);
    temp->setLeft(temp->getRight());
    temp->setRight(node->getRight());
    node->setRight(temp);
    
}
void AVL_Tree::lr_rotation(Node *node){
 
    Node *temp = node->getLeft();
    node->setLeft(temp->getRight());
    temp->setRight(temp->getRight()->getLeft());
    node->getLeft()->setLeft(temp);
    ll_rotation(node);
    
    
}
void AVL_Tree::rr_rotation(Node *node){
    
    
    int value = node->getData();
    Node *temp = node->getRight();
    
    node->setData(temp->getData());
    node->setRight(temp->getRight());
    
    temp->setData(value);
    temp->setRight(temp->getLeft());
    temp->setLeft(node->getLeft());
    node->setLeft(temp);
    
}
void AVL_Tree::rl_rotation(Node *node){
    
    Node *temp = node->getRight();
    node->setRight(temp->getLeft());
    temp->setLeft(node->getRight()->getRight());
    node->getRight()->setRight(temp);
    rr_rotation(node);
    
}



/*
 *Description: balancing the node whoes two children nodes' height difference is greater than 1 or smaller than -1
 *
 *parameters:
 *          1, node;//the node which we want to rotate with, it is the polar point of the rotation
 *
 *
 *return: void
 *
 *
 *
 */
void AVL_Tree::balance(Node *node){
    
    int balance_factor = heightDiff(node);//differences of the node's two sub nodes.
    
    if (balance_factor>1) {//left side is heavy
        
        if (heightDiff(node->getLeft())>0) {//left left case
            
            ll_rotation(node);
            
        }else{//left right case
            
            lr_rotation(node);
        }
        
    }else if (balance_factor<-1){//right side heavy
        
        if (heightDiff(node->getRight())<0) {//right right case
            
            rr_rotation(node);
            
        }else{//right left case
            
            rl_rotation(node);
        }
        
    }
}


/*
 * Description: insert a node into the AVL tree and keep the whole structure balanced after inserting
 *
 *Parameters:
 *          1, Node *node;//the node which needs to be inserted
 *          2, Node *root;//the root of the tree or subtree; 
 *
 *Return: Node *;//the parent node of the inserted node;
 */

Node *AVL_Tree::insert(Node *node, Node *root){
    
    if (this->root == NULL) {
        
        Node *root = new Node();
        root->setLeft(NULL);
        root->setRight(NULL);
        root->setData(node->getData());
        this->root = root;
        
        return root;
    }
    
    if (root == NULL) {
        
        return node;
        
    }else if(node->getData() < root->getData()){
        
        root->setLeft(insert(node, root->getLeft()));
        balance(root);
        
    }else if (node->getData()>=root->getData()){
        
        root->setRight(insert(node, root->getRight()));
        balance(root);
    }
    
    return root;
}

/*
 *Description: print out the sorted nodes of the AVL tree of AVL subtree
 *
 *parameters:
 *          1, Node *node;//the root of the AVL tree of AVL subtree
 *
 *
 */
void AVL_Tree::inorderSort(Node *node){
    
    if (node == NULL) {
        
        return;
    }
    
    inorderSort(node->getLeft());
    std::cout<<node->getData()<<" ";
    inorderSort(node->getRight());
    
}

 


免責聲明!

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



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