二叉树的基本操作(三)——AVL树的性质和插入操作

参考资料:
http://www.cnblogs.com/QG-whz/p/5167238.html

一、AVL树的概念
AVL树的名字来源于它的发明作者G.M. Adelson-Velsky 和 E.M. Landis。AVL树是最先发明的自平衡二叉查找树(Self-Balancing Binary Search Tree,简称平衡二叉树)。查找AVL树的时间复杂度很低,只有O(logN)。
一棵AVL树有如下必要条件
条件一:它必须是二叉查找树
条件二:每个节点的左子树和右子树的高度差的绝对值至多为1
既然这里提到了AVL树的高度差,那么在设计AVL树的结构时,除了二叉树常规的左右节点指针和键值,还应该加入树高。

struct avl_node
{
    int data = 0;
    int height = 1; //初始化树高为1
    avl_node* left_child = NULL;
    avl_node* right_child = NULL;
};

二、AVL树的失衡和恢复
图来自:http://www.th7.cn/system/win/201603/159069.shtml
代码参考了:http://blog.csdn.net/tiantangrenjian/article/details/12891091
如果我们认为:节点是一个一个插入AVL树的,一旦失衡马上调整,那么无论将AVL树的哪个非叶节点当成根节点,在任何时刻最多只有一侧的子树是失衡的。AVL树的失衡要考虑四种情况:
1 根节点左节点的左子树插入了新的元素
《二叉树的基本操作(三)——AVL树的性质和插入操作》

void LL_change(avl_node* &this_node)
{
    avl_node* left_child = this_node->left_child;
    this_node->left_child = left_child->right_child;
    left_child->right_child = this_node;
    this_node = left_child;
    this_node->right_child->height = max(get_height(this_node->right_child->left_child), get_height(this_node->right_child->right_child)) + 1;
    this_node->height = max(get_height(this_node->left_child), get_height(this_node->right_child)) + 1;
}

2 根节点右节点的右子树插入了新的元素
《二叉树的基本操作(三)——AVL树的性质和插入操作》

void RR_change(avl_node* &this_node)
{
    avl_node* right_child = this_node->right_child;
    this_node->right_child = right_child->left_child;
    right_child->left_child = this_node;
    this_node = right_child;
    this_node->left_child->height = max(get_height(this_node->left_child->left_child), get_height(this_node->left_child->right_child)) + 1;
    this_node->height = max(get_height(this_node->left_child), get_height(this_node->right_child)) + 1;
}

3 根节点左节点的右子树插入了新的元素
《二叉树的基本操作(三)——AVL树的性质和插入操作》

void LR_change(avl_node* &this_node)
{
    RR_change(this_node->left_child);
    LL_change(this_node);
}

4 根节点右节点的左子树插入了新的元素
《二叉树的基本操作(三)——AVL树的性质和插入操作》

void RL_change(avl_node* &this_node)
{
    LL_change(this_node->right_child);
    RR_change(this_node);
}

图配合代码应该比较好理解,不需要太多文字叙述。
值得注意的有以下几点:
1) 如果你在写代码之前画下了这些树的变换,但是不能确定自己画得对不对,你可以检查一下,图上最底下四个三角形从左到右的标号顺序是否和变换前相同
2) 在我的代码里,每个函数的形参都是引用的格式,这是因为在变换时当前树的根节点会改变,需要反馈给上一级函数,也有很多程序员喜欢通过让函数返回变换后的根节点,在上一级函数中赋值来达到目的。

三、完整的构造树和插入的代码
下面的代码其实是PAT甲1066题的答案。这题的大意是,依次向空AVL树插入节点,最后输出树的根节点的值。
插入的逻辑很巧妙,需要仔细琢磨一下。我一开始写代码犯的一个逻辑错误就是,一拿到手就开始分析它究竟是LL,LR,RR,RL哪种情况,分类讨论得很费劲还不说,代码还是错的。
正确的做法是,先像常规的二叉树插入节点一样,递归地逐级找到插入的位置,插入完成后,根节点的高度还没有更新,但是子树的高度都更新了,根节点的高度不影响我们判断树是否平衡。如果不平衡,再分类讨论使用哪种情况恢复平衡。最后,子树都能确定是平衡了,再用子树的高度更新根节点的高度。这里先判断平衡与否的巧妙之处在于,避开了高度为0,1,2这种情况下插入的分类讨论,如果是在前两层的范围内插入,无论怎么插入其实都是平衡的,只要简单地插入就行了,高度这么小的树根本没有LL,LR,RR,RL这四种情况。

总得来说,这种递归的代码对我这种不常写递归又对AVL树不甚了解的初学者来说还是有点吃力的,有很多地方需要注意。

#include<iostream>
using namespace std;

struct avl_node
{
    int data = 0;
    int height = 1;
    avl_node* left_child = NULL;
    avl_node* right_child = NULL;
};

int get_height(avl_node* a)
{
    if (a == NULL) return 0;
    else return a->height;
}

int max(int a, int b)
{
    if (a > b) return a;
    else return b;
}

bool balance(avl_node* this_node)
{
    int a = get_height(this_node->left_child);
    int b = get_height(this_node->right_child);
    if (a - b == 1 || b - a == 1 || a == b) return true;
    else return false;
}

void LL_change(avl_node* &this_node)
{
    avl_node* left_child = this_node->left_child;
    this_node->left_child = left_child->right_child;
    left_child->right_child = this_node;
    this_node = left_child;
    this_node->right_child->height = max(get_height(this_node->right_child->left_child), get_height(this_node->right_child->right_child)) + 1;
    this_node->height = max(get_height(this_node->left_child), get_height(this_node->right_child)) + 1;
}

void RR_change(avl_node* &this_node)
{
    avl_node* right_child = this_node->right_child;
    this_node->right_child = right_child->left_child;
    right_child->left_child = this_node;
    this_node = right_child;
    this_node->left_child->height = max(get_height(this_node->left_child->left_child), get_height(this_node->left_child->right_child)) + 1;
    this_node->height = max(get_height(this_node->left_child), get_height(this_node->right_child)) + 1;

}

void LR_change(avl_node* &this_node)
{
    RR_change(this_node->left_child);
    LL_change(this_node);
}

void RL_change(avl_node* &this_node)
{
    LL_change(this_node->right_child);
    RR_change(this_node);
}

void insert(int data,avl_node* &this_node)
{
    if (this_node == NULL)
    {
        this_node = new avl_node;
        this_node->data = data;
    }
    else
    {
        if (data < this_node->data)
        {
            insert(data, this_node->left_child);
            if (!balance(this_node))
            {
                if (data < this_node->left_child->data) LL_change(this_node);
                else LR_change(this_node);
            }
        }
        else
        {
            insert(data, this_node->right_child);
            if (!balance(this_node))
            {
                if (data < this_node->right_child->data) RL_change(this_node);
                else RR_change(this_node);
            }
        }
        this_node->height = max(get_height(this_node->left_child), get_height(this_node->right_child)) + 1;
    }
    return;
}



int main()
{
    int num = 5; //cin >> num;
    int data_formal[20] = { 88, 70, 61, 96, 120 }; //for (int i = 0; i < num; ++i) cin >> data_formal[i]; avl_node* root_node = NULL; for (int i = 0; i < num; ++i) { insert(data_formal[i], root_node); } cout << root_node->data; getchar(); getchar(); return 0; }
    原文作者:AVL树
    原文地址: https://blog.csdn.net/sallyxyl1993/article/details/56496445
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞