[转]AVL树01(c++代码实现)

刘生玺 18/08/14 10:28:51

  前面我们提到输入的数据正好是升序或降序序列时,二叉排序树就会退化成一个单链表,时间复杂度变为 O(N)(如果没看前面,点这里),这是我们所不希望的。我们也提出了解决办法,那就是“平衡”BST树。

   AVL树:最先发明的自平衡二叉查找树。在AVL树中任何节点的两个子树的高度最大差别为1,所以它也被称为高度平衡树。查找、插入和删除在平均和最坏情况下的时间复杂度都是O(log{n}),增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。AVL树得名于它的发明者G. M. Adelson-Velsky和E. M. Landis,他们在1962年的论文《An algorithm for the organization of information》中发表了它。

具体代码实现与分析:

1.节点定义

class Node
{
public:
    int key = 0;
    int height = 0;
    Node *left = nullptr;
    Node *right = nullptr;
    Node(int key_t = 0)
    {
        key = key_t;
        height = 1;
        left = right = nullptr;
    }
};

2. 四种平衡操作

在我们进行完插入或删除操作后,很可能会导致某个结点失去平衡,那么我们就需要把失衡结点旋转一下,使其重新恢复平衡。经过总结,所谓的失衡,不管怎样,只有以下四种情况。

这里写图片描述

(以下统一约定:红色结点为新插入结点,y 结点为失衡结点)
这里写图片描述

左左失衡

所谓的左左,即 “失衡结点” 的左子树比右子树高 2,左孩子(即 x)下的左子树比右子树高 1。

我们只需对 “以 y 为根的子树” 进行 “左左旋转 (ll_rotate)” 即可。一次旋转后,恢复平衡。

Node *AVL::ll_ratate(Node *y)
{
    Node *x = y->left;
    y->left = x->right;
    x->right = y;
    y->height = max(get_height(y->left), get_height(y->right)) + 1;
    x->height = max(get_height(x->left), get_height(x->right)) + 1;
    return x; //返回根节点
}

这里写图片描述

右右失衡:

所谓的右右,即 “失衡结点” 的右子树比左子树高 2,右孩子(即 x)下的右子树比左子树高 1。

我们只需对 “以 y 为根的子树” 进行 “右右旋转 (rr_rotate)” 即可。一次旋转后,恢复平衡。

Node *AVL::rr_ratate(Node *y)
{
    Node *x = y->right;
    y->right = x->left;
    x->left = y;
    y->height = max(get_height(y->left), get_height(y->right)) + 1;
    x->height = max(get_height(x->left), get_height(x->right)) + 1;
    return x;
}

这里写图片描述

左右失衡

所谓的左右,即 “失衡结点” 的左子树比右子树高 2,左孩子(即 x)下的右子树比左子树高 1。

观察发现,若先对 “以 x 为根的子树” 进行 “右右旋转 (rr_rotate)”,此时 “以 y 为根的子树” 恰好符合 “左左失衡”,所以再进行一次 “左左旋转 (ll_rotate)”。两次旋转后,恢复平衡。

Node *AVL::lr_ratate(Node *y)
{
    Node *x = y->left;
    y->left = rr_ratate(x);
    return ll_ratate(y);
}

这里写图片描述

右左失衡

所谓的右左,即 “失衡结点” 的右子树比左子树高 2,右孩子(即 x)下的左子树比右子树高 1。

观察发现,若先对 “以 x 为根的子树” 进行 “左左旋转 (ll_rotate)”,此时 “以 y 为根的子树” 恰好符合 “右右失衡”,所以再进行一次 “右右旋转 (rr_rotate)”。两次旋转后,恢复平衡。

Node *AVL::rl_ratate(Node *y)
{
    Node *x = y->right;
    y->right = ll_ratate(x);
    return rr_ratate(y);
}

3. 插入操作

插入成功后,在递归回溯时依次对经过的结点判断是否失衡,若失衡就需要对其进行对应的旋转操作使其恢复平衡,在这期间,原先作为一棵子树的根结点就会因为旋转被替换,因此设置insert_real()返回的是新根结点,这样就可以实时更新根结点。

void AVL::insert(int key)
{
    header->left = insert_real(key, header->left);
}
Node *AVL::insert_real(int key, Node *node) //返回新的根节点,用来更新根节点
{
    if (node == nullptr)
        return new Node(key);

    if (key < node->key)
        node->left = insert_real(key, node->left);
    else if (key > node->key)
        node->right = insert_real(key, node->right);
    else
        return node;

    node->height = max(get_height(node->left), get_height(node->right)) + 1;
    //因为新加入了一个节点,所以回溯的时候给各个节点高度 +1
    int balance = get_balance(node); //左减右 

    // 左左失衡
    /*即 "失衡结点" 的左子树比右子树高 2,左孩子(即 x)下的左子树比右子树高 1*/
    if (balance > 1 && get_balance(node->left) > 0)
        return ll_ratate(node);

    //右右失衡
    /*"失衡结点" 的右子树比左子树高 2,右孩子(即 x)下的右子树比左子树高 1。*/
    if (balance < -1 && get_balance(node->right) < 0)
        return rr_ratate(node);

    // 左右失衡
    /*"失衡结点" 的左子树比右子树高 2,左孩子(即 x)下的右子树比左子树高 1。。*/
    if (balance > 1 && get_balance(node->left) < 0)
        return lr_ratate(node);

    // 右左失衡
    /*""失衡结点" 的右子树比左子树高 2,右孩子(即 x)下的左子树比右子树高 1。*/
    if (balance < -1 && get_balance(node->right) > 0)
        return rl_ratate(node);

    return node;
}

4. 删除操作

void AVL::erase(int key)
{
    header->left = erase_real(key, header->left);
}
Node *AVL::erase_real(int key, Node *node)
{
    if (node == nullptr)
    {
        cout << key << "不在该 AVL 树中" << endl;
        return node;
    }

    if (key < node->key)
        node->left = erase_real(key, node->left);
    else if (key > node->key)
        node->right = erase_real(key, node->right);
    else
    {
        if (node->left && node->right)
        {
            // 找到后继结点
            Node *x = node->right;
            while (x->left)
                x = x->left;

            // 后继直接复制
            node->key = x->key;

            // 转化为删除后继
            node->right = erase_real(x->key, node->right);
        }
        else
        {
            Node *t = node;
            node = node->left ? node->left : node->right;
            delete t;
            if (node == nullptr)
                return nullptr;
        }
    }

    node->height = max(get_height(node->left), get_height(node->right)) + 1;

    int balance = get_balance(node);

    // 左左失衡
    if (balance > 1 && get_balance(node->left) >= 0) // 需要加等号
        return ll_ratate(node);

    // 右右失衡
    if (balance < -1 && get_balance(node->right) <= 0) // 需要加等号
        return rr_ratate(node);

    // 左右失衡
    if (balance > 1 && get_balance(node->left) < 0)
        return lr_ratate(node);

    // 右左失衡
    if (balance < -1 && get_balance(node->right) > 0)
        return rl_ratate(node);

    return node;
}

为什么需要加等号?因为会出现 get_balance(node->left) == 0 的情况啊,自然必须得去由左左和右右函数去处理。

5.查找操作比较简单,同BST树即可。

6.完整代码:

/*************************************************************************
    > File Name: myhead.h
    > Author: Liu Shengxi 
    > Mail: 13689209566@163.com
    > Created Time: 20180726日 星期四 225958************************************************************************/

#ifndef _MYHEAD_H
#define _MYHEAD_H
#include<vector>
class Node
{
public:
    int key = 0;
    int height = 0;
    Node *left = nullptr;
    Node *right = nullptr;
    Node(int key_t = 0)
    {
        key = key_t;
        height = 1;
        left = right = nullptr;
    }
};
class AVL
{
private:
    Node *header; //header结点并非根结点,header->left指向的才是根结点。

    Node *ll_ratate(Node *y);

    Node *rr_ratate(Node *y);
    Node *lr_ratate(Node *y);
    Node *rl_ratate(Node *y);

    int get_height(Node *node);
    int get_balance(Node *node);

    Node *insert_real(int key, Node *node);
    Node *&find_real(int key, Node *&node);

    Node *erase_real(int key, Node *node);

    void in_order(Node *root);      //中序遍历
    void root_order(Node *root);    // 先序遍历
    void after_order(Node *root); //后序遍历

    int destory(Node *node);

public:
    AVL();
    ~AVL();
    void insert(int key);
    // (递归实现)查找"AVL"中键值为key的节点
    Node *find(int key);

    //(非递归实现)查找"AVL"中键值为key的节点
    Node *loop_find(int key);

    void erase(int key);
    void print(int tag);
};
#endif
#include <iostream>
#include <sstream>
#include <string>
#include "myhead.h"
#include <algorithm>
using namespace std;

AVL::AVL()
{
    header = new Node(-100);
}

AVL::~AVL()
{
    destory(header->left); //将多余的那个节点也删除掉
}

int AVL::destory(Node *p)
{
    if (p == nullptr)
        return 0;
    // test++;
    // cout << "test ==  " << test << endl;
    destory(p->left); //注意先后次序,如果先把p销毁,那么就会找不到p->left,p->right
    destory(p->right);
    delete p;
    p = nullptr;
}

void AVL::insert(int key)
{
    header->left = insert_real(key, header->left);
}
int AVL::get_height(Node *node)
{
    if (node == nullptr)
        return 0;
    return node->height;
}
int AVL::get_balance(Node *node)
{
    if (node == nullptr)
        return 0;
    return get_height(node->left) - get_height(node->right);
}
Node *AVL::insert_real(int key, Node *node) //返回新的根节点,用来更新根节点
{
    if (node == nullptr)
        return new Node(key);

    if (key < node->key)
        node->left = insert_real(key, node->left);
    else if (key > node->key)
        node->right = insert_real(key, node->right);
    else
        return node;

    node->height = max(get_height(node->left), get_height(node->right)) + 1;
    //因为新加入了一个节点,所以回溯的时候给各个节点高度 +1
    int balance = get_balance(node); //左减右

    // 左左失衡
    /*即 "失衡结点" 的左子树比右子树高 2,左孩子(即 x)下的左子树比右子树高 1*/
    if (balance > 1 && get_balance(node->left) > 0)
        return ll_ratate(node);

    //右右失衡
    /*"失衡结点" 的右子树比左子树高 2,右孩子(即 x)下的右子树比左子树高 1。*/
    if (balance < -1 && get_balance(node->right) < 0)
        return rr_ratate(node);

    // 左右失衡
    /*"失衡结点" 的左子树比右子树高 2,左孩子(即 x)下的右子树比左子树高 1。。*/
    if (balance > 1 && get_balance(node->left) < 0)
        return lr_ratate(node);

    // 右左失衡
    /*""失衡结点" 的右子树比左子树高 2,右孩子(即 x)下的左子树比右子树高 1。*/
    if (balance < -1 && get_balance(node->right) > 0)
        return rl_ratate(node);

    return node;
}
void AVL::print(int tag)
{
    if (tag == 1)
    {
        cout << " 先序遍历 : " << endl;
        root_order(header->left);
        cout << endl;
    }
    if (tag == 2)
    {
        cout << " 中序遍历 : " << endl;
        in_order(header->left);
        cout << endl;
    }
    if (tag == 3)
    {
        cout << " 后序遍历 : " << endl;
        after_order(header->left);
        cout << endl;
    }
}
void AVL::after_order(Node *root)
{
    if (root != nullptr)
    {
        after_order(root->left);
        after_order(root->right);
        cout << "[ " << root->key << " ," << root->height << " ]" << endl;
    }
}

void AVL::in_order(Node *root)
{
    if (root != nullptr)
    {
        in_order(root->left); //先打印右子树
        cout << "[ " << root->key << " ," << root->height << " ]" << endl;
        in_order(root->right);
    }
}

void AVL::root_order(Node *root)
{
    if (root != nullptr)
    {
        cout << "[ " << root->key << " ," << root->height << " ]" << endl;
        root_order(root->left); //先打印右子树
        root_order(root->right);
    }
}

Node *AVL::ll_ratate(Node *y)
{
    Node *x = y->left;
    y->left = x->right;
    x->right = y;
    y->height = max(get_height(y->left), get_height(y->right)) + 1;
    x->height = max(get_height(x->left), get_height(x->right)) + 1;
    return x; //返回根节点
}
Node *AVL::rr_ratate(Node *y)
{
    Node *x = y->right;
    y->right = x->left;
    x->left = y;
    y->height = max(get_height(y->left), get_height(y->right)) + 1;
    x->height = max(get_height(x->left), get_height(x->right)) + 1;
    return x;
}
Node *AVL::lr_ratate(Node *y)
{
    Node *x = y->left;
    y->left = rr_ratate(x);
    return ll_ratate(y);
}
Node *AVL::rl_ratate(Node *y)
{
    Node *x = y->right;
    y->right = ll_ratate(x);
    return rr_ratate(y);
}
Node *AVL::find(int key)
{
    return find_real(key, header->left);
}
Node *&AVL::find_real(int key, Node *&node)
{
    if (node == nullptr)
        return node;
    if (key < node->key)
        find_real(key, node->left);
    else if (key > node->key)
        find_real(key, node->right);
    else // 只剩下相等了
        return node;
}

Node *AVL::loop_find(int key)
{
    Node *p = header->left; // p 指向根节点
    while (p && p->key != key)
    {
        if (key < p->key)
            p = p->left;
        else
            p = p->right;
    }
    return p;
}
Node *AVL::erase_real(int key, Node *node)
{
    if (node == nullptr)
    {
        cout << key << "不在该 AVL 树中" << endl;
        return node;
    }

    if (key < node->key)
        node->left = erase_real(key, node->left);
    else if (key > node->key)
        node->right = erase_real(key, node->right);
    else
    {
        if (node->left && node->right)
        {
            // 找到后继结点
            Node *x = node->right;
            while (x->left)
                x = x->left;

            // 后继直接复制
            node->key = x->key;

            // 转化为删除后继
            node->right = erase_real(x->key, node->right);
        }
        else
        {
            Node *t = node;
            node = node->left ? node->left : node->right;
            delete t;
            if (node == nullptr)
                return nullptr;
        }
    }

    node->height = max(get_height(node->left), get_height(node->right)) + 1;

    int balance = get_balance(node);

    // 左左失衡
    if (balance > 1 && get_balance(node->left) >= 0) // 需要加等号
        return ll_ratate(node);

    // 右右失衡
    if (balance < -1 && get_balance(node->right) <= 0) // 需要加等号
        return rr_ratate(node);

    // 左右失衡
    if (balance > 1 && get_balance(node->left) < 0)
        return lr_ratate(node);

    // 右左失衡
    if (balance < -1 && get_balance(node->right) > 0)
        return rl_ratate(node);

    return node;
}

void AVL::erase(int key)
{
    header->left = erase_real(key, header->left);
}
int main(void)
{
    AVL avl;

    // test "insert"
    vector<int> intVec{3, 2, 1, 4, 4, 5, 6, 7, 10, 9, 7, 8};

    for (auto i : intVec)
        avl.insert(i);

    avl.print(1);

    //test "find"
    Node *p = nullptr;
    cout << ((p = avl.find(2)) ? p->key : -1) << endl;   //  2
    cout << ((p = avl.find(100)) ? p->key : -1) << endl; // -1

    cout << ((p = avl.loop_find(14)) ? p->key : -1) << endl; // 测试找不到 -1
    cout << ((p = avl.loop_find(5)) ? p->key : -1) << endl;  // 测试找到 5

    // test "erase"
    avl.erase(100);
    avl.print(2);

    avl.erase(9);
    avl.print(3);

    avl.erase(8);
    avl.print(3);

    return 0;
}

运行结果:
这里写图片描述
这里写图片描述

不知道大家有没有发现,我们的 BF 是从叶子节点向上递增的。(这个只是我们所这样规定的,可以自己改变)

参考链接:https://subetter.com/articles/2018/06/avl-tree.html
最后,为了让读者提提神,列出作为一名合格的程序员还需要学习的其他的树:
这里写图片描述

作者:liushengxi_root 发表于 2018/08/14 10:28:51 原文链接 https://blog.csdn.net/liushengxi_root/article/details/81660375
阅读:112