zoukankan      html  css  js  c++  java
  • AVL树01(c++代码实现)

      前面我们提到输入的数据正好是升序或降序序列时,二叉排序树就会退化成一个单链表,时间复杂度变为 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
    最后,为了让读者提提神,列出作为一名合格的程序员还需要学习的其他的树:
    这里写图片描述

  • 相关阅读:
    常用的长度单位
    HTML常用的列表标签
    html常用文本标签
    CSS选择器及优先级
    块级元素与内联元素
    移动端app打包
    内联框架
    Leetcode303.区域和检索-数组不可变
    Leetcode107.二叉树的层次遍历||
    Leetcode784.字母大小写全排列
  • 原文地址:https://www.cnblogs.com/Tattoo-Welkin/p/10335264.html
Copyright © 2011-2022 走看看