从BinarySearchTree到RedBlackTree之AVL

Posted 立志要去HKUST的金牌插秧王草丛小马子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从BinarySearchTree到RedBlackTree之AVL相关的知识,希望对你有一定的参考价值。

AVL树

没什么好多说的,就是可以优化我们在BST中遇到的有序数组形成的"树"退化成链表的情况。
从中引入了旋转、平衡的概念,并不打算解释,单纯练练代码吧。。。。

最后还有一个大头RedBlackTree,不知道鸽多久。。

code

一开始是LeetCode检查平衡的一个题目,挺好的,建议做一做!

// LeetCode 110 判断一棵树是否为平衡树,带领我们初步地认识AVL TREE

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
/*
class Solution {
public:
    bool isBalanced(TreeNode* root) {
        if(MaxHeight(root) == -1)
            return false;
        return true;
    }

    int MaxHeight(TreeNode* root){
        if(root == nullptr){
            return 0; // 代表到了一个空节点,此时默认高度值为0
        }
        int LeftHeight = MaxHeight(root->left);    // 返回-1的情况就说明自己的左子树不平衡了
        int RightHeight = MaxHeight(root->right);
        if(LeftHeight == -1 || RightHeight == -1 || abs(LeftHeight - RightHeight) > 1){
            // 需要左子树和右子树均平衡并且以自己为根节点的树也是平衡的
            return -1;
        }
        return max(LeftHeight, RightHeight) + 1; // 返回自身的高度
    }
};
*/


/*
全部思路按照清华大学->数据结构C语言版实现:
比较精巧减少工程量的内容->把双旋操作进行了分解,省去了一大堆功夫!
switch用得较多,这也是之前一直缺失的[用的较少的语法]。
遇到的bug->switch的case底下不能定义变量!!!否则编译器就炸了
需要注意的point---->  
1. 一定要理解深刻为什么在函数中我们传递了指针的引用!!因为我们要修改那块指针指向的区域而不单单是值!
2. 一定要深刻理解四种旋转来维护平衡的情况,以及画图来计算平衡后的balance_factor。
*/


#include <iostream>
#include <vector>
#include <queue>
using namespace std;

#define LeftHigher 1
#define RightHigher -1
#define Equal 0

template<typename T>
struct AVLNode{
    T data;
    int balance_factor;    // 按照清华大学数据结构书的定义 平衡因子为左子树高度减去右子树高度!
    AVLNode<T>* left;
    AVLNode<T>* right;
    AVLNode<T>* parent;
    AVLNode(T n, int bf = 0) : data(n), balance_factor(bf), left(nullptr), right(nullptr), parent(nullptr){};
};

template<typename T>
class AVLTREE{
private:
    /*
    实际上我们的私有属性root是不需要的,因为"旋转"的性质导致我们的root始终在调整变化!
    */
    AVLNode<T> *root;
    bool taller = false;
public:
    AVLTREE() { root = nullptr; };
    AVLNode<T> *&getroot() { return root; };
    void insert(AVLNode<T>* &root, T data);  // 因为这里要递归地去进行插入操作,所以应该有一个当前root结点,非类的root
    bool deletenode(T data);
    bool search(AVLNode<T>* root, T data);
    void R_Rotate(AVLNode<T>* &_root);
    void L_Rotate(AVLNode<T>* &_root);
    // 定义了上面两种旋转方式,但实际上还有"双旋"的情况,但是双旋是可以分步完成的!分别是不同情况的失衡时的解决策略
    void LeftBalance(AVLNode<T>* &_root);
    void RightBalance(AVLNode<T>* &_root);
    // 展示是否有序!
    void MidOrderTraverse(AVLNode<T> *_root);
    int GetHeight(AVLNode<T> *_root);
};

template<typename T>
bool AVLTREE<T>::search(AVLNode<T>* _root, T data){
    // 这里和BSTREE的实现方式不一样,我们用递归的方式来寻找
    if(_root){
        if(_root->data == data){
            return true;
        }
        else if(_root->data > data){
            return search(_root->left, data);
        }
        else{
            return search(_root->right, data);
        }
    }
    return false;
}

template<typename T>
void AVLTREE<T>::R_Rotate(AVLNode<T>* &_root){
    AVLNode<T> *left_sub_tree = _root->left;
    _root->left = left_sub_tree->right;
    left_sub_tree->right = _root;
    _root = left_sub_tree;
}

template<typename T>
void AVLTREE<T>::L_Rotate(AVLNode<T>* &_root){
    AVLNode<T>* right_sub_tree = _root->right;
    _root->right = right_sub_tree->left;
    right_sub_tree->left = _root;
    _root = right_sub_tree;
}

template<typename T>
void AVLTREE<T>::LeftBalance(AVLNode<T>* &_root){
    AVLNode<T> *leftsubtree = _root->left;
    AVLNode<T> *rightsubtree = leftsubtree->right;
    switch (leftsubtree->balance_factor)
    {
    case LeftHigher:
        /*
        考虑这种情况,遇到第一个结点不平衡,且是因为左边过高才调用的LeftBalance函数。
        在此基础上自己的左子树根结点的平衡因子也是左高,就是我们的 朝左链成一条的不平衡情况
        此时只需做一个右旋就可,且旋转完两者均平衡!
        */
        _root->balance_factor = Equal;
        leftsubtree->balance_factor = Equal;
        R_Rotate(_root);
        break;
    
    case RightHigher:
        // 根据不同的情况先设置这些个结点的balance_factor
        switch (rightsubtree->balance_factor)
        {
        case Equal:
            _root->balance_factor = Equal;
            leftsubtree->balance_factor = Equal;
            break;
        
        case LeftHigher:
            rightsubtree->balance_factor = leftsubtree->balance_factor = Equal;
            _root->balance_factor = RightHigher;
            break;
        
        case RightHigher:
            _root->balance_factor = rightsubtree->balance_factor = Equal;
            leftsubtree->balance_factor = LeftHigher;
            break;

        default:
            break;
        }
        L_Rotate(_root->left);
        R_Rotate(_root);
        break;

    default:
        break;
    }
}

template<typename T>
void AVLTREE<T>::RightBalance(AVLNode<T>* &_root){
    AVLNode<T> *rightsubtree = _root->right;
    AVLNode<T> *leftsubtree = rightsubtree->left;
    switch (rightsubtree->balance_factor)
    {
    case RightHigher:
        _root->balance_factor = Equal;
        rightsubtree->balance_factor = Equal;
        L_Rotate(_root);
        break;
    
    case LeftHigher:
        // 根据不同的情况先设置这些个结点的balance_factor
        switch (leftsubtree->balance_factor)
        {
        case Equal:
            _root->balance_factor = Equal;
            rightsubtree->balance_factor = Equal;
            break;
        
        case LeftHigher:
            _root->balance_factor = leftsubtree->balance_factor = Equal;
            rightsubtree->balance_factor = RightHigher;
            break;
        
        case RightHigher:
            rightsubtree->balance_factor = leftsubtree->balance_factor = Equal;
            _root->balance_factor = LeftHigher;
            break;

        default:
            break;
        }
        R_Rotate(_root->right);
        L_Rotate(_root);
        break;

    default:
        break;
    }
}

template<typename T>
void AVLTREE<T>::insert(AVLNode<T>* &_root, T data){
    if(!_root){                    // 用指针的引用可以直接连接到树上   因为指针的引用可以改变原父结点的孩子的地址指向!
        _root = new AVLNode<T>(data);
        taller = true;
    }
    else{
        if(_root->data > data){
            // _root->balance_factor += 1;
            insert(_root->left, data);
            if(taller){
                switch (_root->balance_factor)
                {
                case RightHigher:
                    _root->balance_factor = Equal;
                    taller = false;
                    break;
                
                case Equal:
                    _root->balance_factor = LeftHigher;
                    taller = true;
                    break;

                case LeftHigher:
                    LeftBalance(_root);
                    taller = false;
                    break;

                default:
                    break;
                }
            }
        }
        else{
            // _root->balance_factor -= 1;
            insert(_root->right, data);
            if(taller){
                switch (_root->balance_factor)
                {
                case LeftHigher:
                    _root->balance_factor = Equal;
                    taller = false;
                    break;

                case Equal:
                    _root->balance_factor = RightHigher;
                    taller = true;
                    break;
                
                case RightHigher:
                    RightBalance(_root);
                    taller = false;
                    break;

                default:
                    break;
                }
            }
        }
    }
}

template<typename T>
void AVLTREE<T>::MidOrderTraverse(AVLNode<T>* _root){
    if(_root){
        MidOrderTraverse(_root->left);
        cout << _root->data << " ";
        MidOrderTraverse(_root->right);
    }
}

template<typename T>
int AVLTREE<T>::GetHeight(AVLNode<T>* _root){
    // 这里利用层序遍历【队列的方法】来进行求解高度
    if(!_root)
        return 0;
    queue<AVLNode<T> *> que;
    que.push(_root);
    int height = 0;
    while(!que.empty()){
        for (size_t i = 0; i < que.size(); i ++){
            AVLNode<T> *temp = que.front();
            if(temp->left)
                que.push(temp->left);
            if(temp->right)
                que.push(temp->right);
            que.pop();
        }
        height++;
    }
    return height;
}

int main(){
    AVLTREE<int> tree;
    AVLNode<int>* root = tree.getroot();
    vector<int> vec{-4, 0, 7, 4, 9, -6, -1, -7};
    vector<int> vec2{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};   // 用一个有序数组,对比BST和AVL树,性能差别就很明显了!
    // 在BST里会是一条长度为10[即树的高度]的链表,但在AVL中,树高仅为4!! 这显然好很多,搜索复杂度就牛逼起来了!!

    for(auto num : vec2){
        if(!tree.search(root, num))
            tree.insert(root, num);
        else{
            cout << "It has been in the tree!" << endl;
        }
    }
    cout << "current root is:" << root->data << endl;
    cout << "height of the tree is:" << tree.GetHeight(root) << endl;
    tree.MidOrderTraverse(root);
    return 0;
}

以上是关于从BinarySearchTree到RedBlackTree之AVL的主要内容,如果未能解决你的问题,请参考以下文章

从BinarySearchTree到RedBlackTree之AVL

从BinarySearchTree到RedBlackTree之BST

从BinarySearchTree到RedBlackTree之BST

从BinarySearchTree到RedBlackTree之BST

javascript binarySearchTree由plastikaweb创建 - https://repl.it/@plastikaweb/binarySearchTree

BinarySearchTree-二叉搜索树