数据结构-考研难点代码突破(C++实现树型查找 - 平衡二叉树(AVL树)的基本操作(增删))

1. 平衡二叉树的概念

二叉搜索树虽然可以提高搜索效率,但如果数据接近有序的话搜索二叉树的效率退化为链表了。为了解决这个问题,提出了AVL树。

向平衡二叉树中插入新节点,保证每个节点的高度差的绝对值小于等于1。降低树的高度,提高搜索效率。这种树称为AVL树

为了确定插入或者删除节点后,这棵树的高度差的绝对值,这里给树的每一个节点引入平衡因子。
每个节点的平衡因子=这个节点的右子树高度-这个节点的左子树高度。

所以根据AVL树的定义:当平衡因子变为2或-2时,需要调整AVL树

AVL树的插入

情况一:右单旋(新节点插入较高左子树的左侧)
在这里插入图片描述
插入新节点后,b节点平衡因子为-1,a节点平衡因子为-2。

调整步骤:将a节点连接到b右节点,将原来b右子树连接到a左子树上,这样a,b节点的平衡因子又变回0

情况二:左单旋(新节点插入较高右子树的右侧)
在这里插入图片描述
插入新节点后,b节点平衡因子为1,a节点平衡因子为2

旋转思路:将bL连接到a的右树上,a再连接到b的左树上。调整平衡因子a,b平衡因子都变为0,调整a,b节点父指针,调整树的根节点,处理子树的情况,细节和右单旋类似。这样a,b节点的平衡因子又变回0

情况三:左右双旋(新节点插入较高左子树的右侧)
在这里插入图片描述
观察上图,相当于把c节点拆开c做根节点,c的左子树给b右树,c的右子树给a的左树。c的左子树连接b,c的右子树连接a。从而达到降低树的高度的目的。

根据上面的分析可知c在旋转后平衡因子一定为0.
如果新节点插入的位置在c的左子树上,经过旋转会到b的右子树上,b的平衡因子为0,a的平衡因子为1.
如果新节点插入的位置在c的右子树上,经过旋转会到a的左子树上,b的平衡因子为-1,a的平衡因子为0(如上图).

特殊情况:当b节点没有子树时,即这颗树只有cba这三个节点,此时a,b,c这三个节点的平衡因子都为0

情况四:右左双旋(新节点插入较高右子树的左侧)
在这里插入图片描述
由上图可以看到a节点的平衡因子为2,b节点的平衡因子为-1时要进行右左双旋。
特点为:b节点进行右旋,a节点进行左旋。

AVL树查找效率

如果树高为h,最坏情况下,查找需要对比h次

平衡二叉树上任一结点的左子树和右子树的高度之差不超过1。

假设以n(h)表示深度为h的平衡树中含有的最少结点数。

n(0)=0此时是空树,n(1)=1此时树只有根节点,n(2)=2……

高度为h的平衡二叉树,最少节点个数为n(h)=n(h-1)+n(h-2)+1

所以9个节点的平衡二叉树i有四层,最坏需要对比4次就可以查找到对应元素

所以平衡二叉树的事件复杂度为树高O(logN)

AVL树的删除(了解)

AVL树的删除操作与插入类似;

  1. 首先先按照二叉搜索树的删除规则进行删除
  2. 之后向上更新平衡因子,如果遇到不平衡的情况和AVL树插入操作类似进行树结构的调整,调整规则完全相同

AVL树的删除,关键就是平衡因子的调整。调整完毕后,根据平衡因子的值,选择旋转方式,并向上进行传递。

我这里使用:每次删除后,从删除根节点开始调整平衡因子,如果失去了AVL树形状,进行反转,之后依次向上递归调整。类似后续遍历的方式。

从整棵树的最下方从头开始调整。

这个方法有个弊端,就是每次删除必须向上判断平衡因子是否正确,效率比较低

2. C++代码

BalanceTree类

#include <iostream>
#include <vector>
#include <algorithm>

struct Node
{
    int _bf;
    Node *_left;
    Node *_right;
    Node *_parent;

    int _val;
    Node(int val) : _bf(0), _left(nullptr), _right(nullptr), _parent(nullptr), _val(val) {}
};

class BalanceTree
{
    Node *root;

public:
    ~BalanceTree() {}

    void InorderDisplay()
    {
        _DisPlay(root);
        std::cout << "\n";
    }
    BalanceTree(const std::vector<int> buff)
    {
        for (auto &num : buff)
        {
            insert(num);
        }
    }
    bool insert(int val)
    {
        // 不允许值重复
        if (root == nullptr)
        {
            root = new Node(val);
            return true;
        }
        else
        {
            Node *prev = nullptr;
            Node *cur = root;
            while (cur != nullptr)
            {
                prev = cur;
                if (cur->_val < val)
                {
                    cur = cur->_right;
                }
                else if (cur->_val > val)
                {
                    cur = cur->_left;
                }
                else
                {
                    // 数据冗余
                    return false;
                }
            }
            // cur==nullptr
            cur = new Node(val);

            if (prev->_val > val)
                prev->_left = cur;
            else
                prev->_right = cur;

            cur->_parent = prev;

            // 更新平衡因子
            while (prev != nullptr)
            {
                // 判断插入位置
                if (prev->_left == cur)
                    prev->_bf--;
                else if (prev->_right == cur)
                    prev->_bf++;

                if (prev->_bf == 0)
                {
                    break; // 插入后仍然是AVL树
                }
                else if (prev->_bf == -1 || prev->_bf == 1)
                {
                    cur = prev;
                    prev = prev->_parent; // 这次插入影响高度,需要向上调整
                }
                else if (prev->_bf == -2 || prev->_bf == 2)
                {
                    // 不是AVL树需要调整高度
                    if (prev->_bf == -2)
                    {
                        if (cur->_bf == -1)
                        {
                            // 右单旋
                            TurnRight(prev);
                        }
                        else if (cur->_bf == 1)
                        {
                            // 左右双旋
                            TurnLeftRight(prev);
                        }
                    }
                    else if (prev->_bf == 2)
                    {
                        if (cur->_bf == -1)
                        {
                            // 右左双旋
                            TurnRightLeft(prev);
                        }
                        else if (cur->_bf == 1)
                        {
                            // 左单旋
                            TurnLeft(prev);
                        }
                    }
                }
            }
            return true;
        }
    }

    // 判断一棵树是否是AVL树
    bool isAVLTree()
    {
        return _isAVLTree(root);
    }

    void erase(int val)
    {
        _erase(val, root);
        _adjust(root);
    }

private:
    void _adjust(Node *node)
    {
        // 后续遍历调整搜索二叉树为AVL树
        if (node == nullptr)
        {
            return;
        }
        _adjust(node->_left);
        _adjust(node->_right);
        int leftHeight = _height(node->_left);
        int rightHeight = _height(node->_right);
        node->_bf = rightHeight - leftHeight;

        if (node->_bf == 2 || node->_bf == -2)
        {
            Node *left = node->_left;
            Node *right = node->_right;

            if (node->_bf == -2)
            {
                if ((left != nullptr && left->_bf == -1) || (right != nullptr && right->_bf == -1))
                {
                    // 右旋
                    TurnRight(node);
                }
                else if ((left != nullptr && left->_bf == 1) || (right != nullptr && right->_bf == 1))
                {
                    // 左右双旋
                    TurnLeftRight(node);
                }
            }
            else if (node->_bf == 2)
            {
                if ((left != nullptr && left->_bf == -1) || (right != nullptr && right->_bf == -1))
                {
                    // 右左旋
                    TurnRightLeft(node);
                }
                else if ((left != nullptr && left->_bf == 1) || (right != nullptr && right->_bf == 1))
                {
                    // 左旋
                    TurnLeft(node);
                }
            }
        }
    }
    // 搜索二叉树删除方式
    void _erase(int val, Node *&node)
    {
        // 删除节点值为val的节点,并且返回删除后这个节点的指针
        if (node == nullptr)
        {
            return;
        }

        else
        {
            if (node->_val > val)
            {
                _erase(val, node->_left);
            }
            else if (node->_val < val)
            {
                _erase(val, node->_right);
            }
            else
            {
                if (node->_left == nullptr)
                {
                    Node *del = node;
                    node = node->_right;
                    if (node != nullptr)
                        node->_parent = del->_parent;
                    delete del;
                }
                else if (node->_right == nullptr)
                {
                    Node *del = node;
                    node = node->_left;
                    if (node != nullptr)
                        node->_parent = del->_parent;
                    delete del;
                }
                else
                {
                    // 找要删除节点的后继
                    Node *right_min_node = node->_right;
                    while (right_min_node->_left != nullptr)
                    {
                        right_min_node = right_min_node->_left;
                    }

                    // 记录right_min_node这个节点的值,吧这个节点的值和node节点交换,在删除right_min_node这个节点即可
                    // right_min_node这个节点的左子树一定为空,在上面顶点流程会处理
                    int tmp = right_min_node->_val;
                    _erase(tmp, node->_right);
                    node->_val = tmp;
                }
            }
        }
    }
    void _DisPlay(Node *root)
    {
        if (root == nullptr)
            return;
        _DisPlay(root->_left);
        std::cout << root->_val << " ";
        _DisPlay(root->_right);
    }

    int _height(Node *node)
    {
        if (node == nullptr)
            return 0;

        int leftHeight = _height(node->_left);
        int rightHeight = _height(node->_right);
        return std::max(leftHeight, rightHeight) + 1;
    }

    bool _isAVLTree(Node *node)
    {
        if (node == nullptr)
        {
            return true;
        }
        int leftHigh = _height(node->_left);
        int rightHigh = _height(node->_right);

        if (node->_bf != rightHigh - leftHigh)
        {
            std::cout << "ERROR:BF " << node->_bf << std::endl;
            return false;
        }

        return std::abs(rightHigh - leftHigh) < 2 && _isAVLTree(node->_left) && _isAVLTree(node->_right);
    }
    /**
     * 旋转代码结合博客流程对照
     */

    // 右单旋
    void TurnRight(Node *node)
    {
        Node *left = node->_left;
        Node *right = left->_right;

        node->_left = right;
        if (right != nullptr)
        {
            right->_parent = node;
        }

        left->_right = node;
        Node *parent = node->_parent; // 原父母
        node->_parent = left;

        // 修改平衡因子
        node->_bf = 0;
        left->_bf = 0;

        if (node == root)
        {
            // 旋转根节点
            root = left;
            root->_parent = parent;
        }
        else
        {
            left->_parent = parent;
            if (parent->_left == node)
            {
                parent->_left = left;
            }
            else
            {
                parent->_right = left;
            }
        }
    }

    // 左单旋
    void TurnLeft(Node *node)
    {
        Node *right = node->_right;
        Node *left = right->_left;

        node->_right = left;
        if (left != nullptr)
        {
            left->_parent = node;
        }

        right->_left = node;
        Node *parent = node->_parent;
        node->_parent = right;

        node->_bf = 0;
        right->_bf = 0;

        if (node == root)
        {
            node = right;
            right->_parent = parent;
        }
        else
        {
            right->_parent = parent;
            if (parent->_left == node)
            {
                parent->_left = right;
            }
            else
            {
                parent->_right = right;
            }
        }
    }

    // 左右双旋
    void TurnLeftRight(Node *node)
    {
        Node *left = node->_left;
        Node *right = left->_right;

        TurnLeft(left);
        TurnRight(node);

        // 调整平衡因子
        if (right->_bf == 1)
        {
            // 新节点插入到right节点的右子树
            right->_bf = 0;
            left->_bf = -1;
            node->_bf == 0;
        }
        else if (right->_bf == -1)
        {
            // 新节点插入到right节点的左子树上
            right->_bf = 0;
            left->_bf = 0;
            node->_bf = 1;
        }
        else if (right->_bf = 0)
        {
            right->_bf = 0;
            left->_bf = 0;
            node->_bf = 0;
        }
    }

    // 右左双旋
    void TurnRightLeft(Node *node)
    {
        Node *right = node->_right;
        Node *left = right->_left;

        TurnRight(right);
        TurnLeft(node);

        if (left->_bf == 1)
        {
            left->_bf = 0;
            right->_bf = 0;
            node->_bf = -1;
        }
        else if (left->_bf == -1)
        {
            left->_bf = 0;
            right->_bf = 1;
            node->_bf = 0;
        }
        else if (left->_bf = 0)
        {
            left->_bf = 0;
            right->_bf = 0;
            node->_bf = 0;
        }
    }
};

测试代码:

#include "BalanceTree.h"

using namespace std;

int main(int argc, char const *argv[])
{
    BalanceTree tree({3, 4, 2, 5, 6, 1, 7});
    tree.InorderDisplay();
    std::cout << tree.isAVLTree() << std::endl;
    tree.erase(4);
    tree.InorderDisplay();
    std::cout << tree.isAVLTree() << std::endl;
    tree.erase(5);
    tree.InorderDisplay();
    std::cout << tree.isAVLTree() << std::endl;
    return 0;
}

运行结果:
在这里插入图片描述

3. 考研数据结构代码仓库

考研数据结构代码仓库

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

NUC_Dodamce

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值