二叉平衡搜索树AVL树

一、AVL树的节点平衡旋转操作

AVL树也叫二叉平衡搜索树,增删查都可以达到log(n)

在这里插入图片描述

1. 右旋
get_h(node->left_->left_) >= get_h(node->left_->right_)

在这里插入图片描述

2. 左旋
get_h(node->right_->right_) >= get_h(node->right_->left_)

在这里插入图片描述

3. 左 - 右旋转
get_h(node->left_->right_) >= get_h(node->left_->left_)

在这里插入图片描述
左 - 右旋转就是先以node的左child为轴进行左旋,然后以node为轴进行右旋

4. 右 - 左旋转
get_h(node->right_->left_) >= get_h(node->right_->right_)

在这里插入图片描述
右 - 左旋转就是先以node的右child为轴进行右旋,然后以node为轴进行左旋

二、AVL树的代码实现

1. insert实现

对于insert来说,因为插入的节点都是叶子节点,最多旋转两次即可保证平衡。插入节点后需要注意检验当前节点是否平衡,并进行相应的旋转操作

2. remove实现

前驱节点: 当前节点左子树中值最大的节点
后继节点: 当前节点右子树中值最小的节点
前驱节点和后继节点特点:最多有一个孩子

删除节点的时候需要分情况:

  • 待删除节点有2个孩子,若左子树高,则用当前节点的前驱节点的值替换掉待删除节点的值,然后删除前驱节点。若右子树高,则用当前节点的后继节点的值替换掉待删除节点的值,然后删除后继节点。谁高删谁可以保证删除后当前节点所有子孙节点都平衡。
  • 待删除节点有1个孩子,将这个孩子节点的地址写入(待删除节点的)父节点对应的地址域
  • 待删除节点没有孩子,父节点对应的地址域置空

删除节点后需要验证平衡,并且进行相应的旋转操作。边回溯边检查平衡,进行旋转调整,直到回溯到根节点

template<typename T>
class AVLTree {
public:
    // 二叉平衡搜索树 
    AVLTree() : root_(nullptr) {}

    ~AVLTree() {
        if (root_ != nullptr) {
            queue <Node*> q;
            q.push(root_);
            while (!q.empty()) {
                Node* cur = q.front();
                q.pop();
                if (cur->left_ != nullptr) {
                    q.push(cur->left_);
                }
                if (cur->right_ != nullptr) {
                    q.push(cur->right_);
                }
                delete cur;
            }
        }
    }

    void insert(const T& val) {
        root_ = insert(root_, val);
    }

    void remove(const T& val) {
        root_ = remove(root_, val);
    }
private:
    struct Node {
        Node(T data = T())
            : data_(data)
            , left_(nullptr)
            , right_(nullptr)
            , height_(1)
        {};

        T data_;
        Node* left_;
        Node* right_;
        int height_;  // 当前节点的高度值
    };

    // 避免节点为nullptr,无法获取height_属性
    int get_h(Node* node) {
        return node == nullptr ? 0 : node->height_;
    }

    // 左旋转操作:以节点node为轴做左旋转操作,并返回新的根节点
    Node* left_rotate(Node* node) {
        Node* child = node->right_;
        node->right_ = child->left_;
        child->left_ = node;

        // 高度更新
        node->height_ = max(get_h(node->left_), get_h(node->right_)) + 1;
        child->height_ = max(get_h(child->left_), get_h(child->right_)) + 1;
        // 由于树的节点是堆区的,可以返回这个指针,不会受到栈帧清退的影响。这里指针返回,只是值传递,就是把节点的地址当作值传递了
        return child;
    }

    // 右旋转操作:以节点node为轴做右旋转操作,并返回新的根节点
    Node* right_rotate(Node* node) {
        Node* child = node->left_;
        node->left_ = child->right_;
        child->right_ = node;
        
        // 高度更新
        node->height_ = max(get_h(node->left_), get_h(node->right_)) + 1;
        child->height_ = max(get_h(child->left_), get_h(child->right_)) + 1;
        // 由于树的节点是堆区的,可以返回这个指针,不会受到栈帧清退的影响。这里指针返回,只是值传递,就是把节点的地址当作值传递了
        return child;   
    }

    // 左-右旋转:以节点node为轴做左-右旋转操作,并返回新的根节点
    Node* left_right_rotate(Node* node) {
        node->left_ = left_rotate(node->left_);
        return right_rotate(node);
    }

    // 右-左旋转:以节点node为轴做右-左旋转操作,并返回新的根节点
    Node* right_left_rotate(Node* node) {
        node->right_ = right_rotate(node->right_);
        return left_rotate(node);
    }

    // AVL树 = BST + 平衡
    // insert代码在BST树的基础上添加3块代码即可成为AVL树,插入节点后的回溯过程中需要检查当前时候失衡并旋转,旋转完成后需要检查并更新当前节点高度
    Node* insert(Node* node, const T& val) {
        if (node == nullptr) {
            return new Node(val);
        }
        if (val == node->data_) {
            return node;
        } else if (val < node->data_) {
            // 插入新的叶子节点
            node->left_ = insert(node->left_, val);
            // 插入叶子节点后可能导致某个祖先节点不平衡,所以是在递归回溯的过程中检查进行平衡操作
            // 往左边插入节点,若失衡,肯定是node的左子树太高导致node失衡
            // 添加1
            if (get_h(node->left_) - get_h(node->right_) > 1) {
                // 当前的node失衡分两种
                if (get_h(node->left_->left_) >= get_h(node->left_->right_)) {
                    // 左孩子的左子树高,右旋
                    node = right_rotate(node);
                }
                else {
                    // 左孩子的右子树高,左-右旋
                    node = left_right_rotate(node);
                }
            }
        }
        else {
            node->right_ = insert(node->right_, val);
            /*
                其实等价于:
                if(node->right_ == nullptr){
                    node->right_ = insert(node->right_, val);
                }else{
                    insert(node->right_, val);
                }
            */
            // 添加2
            if (get_h(node->right_) - get_h(node->left_) > 1) {
                if (get_h(node->right_->right_) >= get_h(node->right_->left_)) {
                    // 右孩子的右子树高,左旋
                    node = left_rotate(node);
                }
                else {
                    // 右孩子的左子树高,右-左旋
                    node = right_left_rotate(node);
                }
            }
        }
        // 添加3 由于子树中增加了新的节点,在回溯时应该检查并更新当前节点的高度
        node->height_ = max(get_h(node->left_), get_h(node->right_)) + 1;
        return node;
    }

    Node* remove(Node* node, const T& val) {
        if (nullptr == node) {
            // 没找到这个val
            return nullptr;
        }
        if (val < node->data_) {
            node->left_ = remove(node->left_, val);
            // 添加1
            // 删除后验证平衡,左子树删除节点,可能造成右子树太高
            if (get_h(node->right_) - get_h(node->left_) > 1) {
                if (get_h(node->right_->right_) >= get_h(node->right_->left_)) {
                    // 右孩子的右子树太高,左旋
                    node = left_rotate(node);
                }
                else {
                    // 右孩子的左子树太高,右-左旋
                    node = right_left_rotate(node);
                }
            }
        }
        else if (val > node->data_) {
            node->right_ = remove(node->right_, val);
            // 添加2
            // 删除后验证平衡,右子树删除节点,可能造成左子树太高
            if (get_h(node->left_) - get_h(node->right_) > 1) {
                if (get_h(node->left_->left_) >= get_h(node->left_->right_)) {
                    // 左孩子的左子树太高,右旋
                    node = right_rotate(node);
                }
                else {
                    // 左孩子的右子树太高,左-右旋
                    node = left_right_rotate(node);
                }
            }
        }
        else {
            // 找到了val
            // 先处理当前节点有两个孩子的情况
            if (node->left_ != nullptr && node->right_ != nullptr) {
                // 添加3
                if (get_h(node->left_) >= get_h(node->right_)) {
                	// 谁高删谁可以保证当前节点的子孙节点都平衡,但是不能保证当前节点的祖先节点都平衡
                    // 左子树高,删除前驱
                    Node* pre = node->left_;
                    while (pre->right_ != nullptr) {
                        pre = pre->right_;
                    }
                    // 值替换后,删除前驱节点的值
                    node->data_ = pre->data_;
                    node->left_ = remove(node->left_, pre->data_);
                }
                else {
                    // 右子树高,删除后继
                    Node* post = node->right_;
                    while (post->left_ != nullptr) {
                        post = post->left_;
                    }
                    // 值替换后,删除后继节点的值
                    node->data_ = post->data_;
                    node->right_ = remove(node->right_, post->data_);
                }
            }
            else {
                // 待删除的节点最多只有一个孩子
                if (node->left_ != nullptr) {
                    Node* child = node->left_;
                    delete node;
                    return child;
                }
                else if (node->right_ != nullptr) {
                    Node* child = node->right_;
                    delete node;
                    return child;
                }
                else {
                    delete node;
                    return nullptr;
                }
            }
            
        }

        // 添加4
        // 更新节点高度
        node->height_ = max(get_h(node->left_), get_h(node->right_)) + 1;
        // 把当前这个节点返回给父节点
        return node;
    }
    Node* root_;
};

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 二叉平衡搜索是一种特殊的二叉搜索,它的特点是插入、删除、查找的时间复杂度都是 O(logn)。它能够保证的高度尽量平衡,从而达到最优的时间复杂度。 在插入操作中,首先将新节点插入到二叉搜索中,然后判断是否满足二叉平衡的性质(即任意节点的左右子的高度差不超过 1)。如果不满足,就需要进行调整,使得重新平衡。具体的调整方法可以使用旋转操作,包括左旋、右旋和双旋。 例如,当向二叉平衡搜索中插入一个新节点时,如果发现它导致了的不平衡,就可以进行旋转操作,使得重新平衡。 不过,需要注意的是,并不是所有的二叉搜索都可以转换为二叉平衡搜索,因为平衡的插入、删除、查找操作都要比普通的二叉搜索复杂一些。 ### 回答2: 二叉平衡搜索AVL树)是一种自平衡二叉搜索,为了保持平衡性,需要在插入新节点时进行一系列的旋转和调整。 插入操作的过程如下: 1. 首先,将新节点插入到二叉搜索中的适当位置,以确保的有序性。 2. 接着,从插入点开始向上回溯,更新所有经过的祖先节点的平衡因子。 - 若某个节点的左子高度大于右子高度,则将该节点的平衡因子设为1。 - 若某个节点的右子高度大于左子高度,则将该节点的平衡因子设为-1。 - 若某个节点的左子高度与右子高度相等,则将该节点的平衡因子设为0。 - 如果平衡因子的绝对值大于1,则表示该节点失衡。 3. 如果某个节点失衡,需要通过旋转和重新连接来恢复平衡。 - 左旋:当一颗的右子高度较高时,对进行左旋操作。 - 右旋:当一颗的左子高度较高时,对进行右旋操作。 - 先左旋后右旋:当一颗的左子的右子高度较高时,先对左子进行左旋操作,然后对整个进行右旋操作。 - 先右旋后左旋:当一颗的右子的左子高度较高时,先对右子进行右旋操作,然后对整个进行左旋操作。 4. 一直回溯到根节点,直到中所有的节点平衡因子都被更新。 通过以上的插入操作,AVL树可以保持平衡,提高搜索效率。 ### 回答3: 二叉平衡搜索是一种特殊的二叉搜索,它通过自动调整节点的位置来保持平衡性,从而提高搜索和插入操作的效率。以下是二叉平衡搜索如何插入新节点的步骤: 1. 首先,根据二叉搜索的性质,找到新节点应该插入的位置。从根节点开始,比较新节点的值与当前节点的值大小关系,如果新节点的值小于当前节点的值,则继续在当前节点的左子中比较;如果新节点的值大于当前节点的值,则继续在当前节点的右子中比较,直到找到一个合适的插入位置。 2. 插入新节点。一旦找到合适的插入位置,将新节点插入为当前节点的子节点。如果新节点的值小于当前节点的值,则插入为当前节点的左子节点;如果新节点的值大于当前节点的值,则插入为当前节点的右子节点。 3. 调整平衡性。插入新节点后,需要检查整个是否满足平衡性要求。如果不满足平衡性,则需要进行相应的旋转操作来调整的结构。旋转操作通常分为左旋和右旋两种类型,具体选择哪种旋转操作取决于的结构和当前节点的位置。 4. 更新节点信息。在插入节点和调整平衡后,需要更新相关节点的高度信息。中每个节点通常都包含一个记录节点高度的属性,该属性用于判断平衡性。插入新节点后,需要逐层向上更新节点的高度信息,直到根节点。 5. 完成插入操作。当整个插入操作完成后,即成功将新节点插入到二叉平衡搜索中,并且保持了平衡性。 总之,二叉平衡搜索的插入过程包括找到插入位置、插入新节点、调整平衡、更新节点信息等步骤。通过这些步骤,我们可以保证二叉平衡搜索平衡性,并且使插入操作效率更高。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

bugcoder-9905

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

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

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

打赏作者

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

抵扣说明:

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

余额充值