AVL 树

本文介绍了二叉查找树(BST)的基础概念,特别是AVL树的平衡因子及其维护方法。通过右旋和左旋操作演示了如何在插入或删除节点后保持树的平衡。代码示例详细展示了AVL树的节点结构、高度计算、平衡因子获取以及旋转操作的实现。

二叉查找树(BST)

平衡二叉树

在这里插入图片描述
在这里插入图片描述

  • 平衡因子: 某个结点的左子树的高度减去右子树的高度得到的差值。
  • 插入或删除节点后,可能会造成 AVL 树的平衡被破坏,因此,需要沿着从被插入/删除的节点到根的路径对树进行维护。就是在树的某一部分的不平衡度超过一个阈值后触发相应的平衡操作,保证树的平衡度在可以接受的范围内。
//返回节点高度
    int getHeight(const Node *const node) const noexcept {
        if (node == nullptr) {
            return 0;
        }
        return node->height;
    }
    //返回平衡因子
    int getBalanceFactor(const Node *const node) const noexcept {
        if (node == nullptr) {
            return 0;
        }
        return getHeight(node->left) - getHeight(node->right);
    }

右旋转

在这里插入图片描述

Node *leftRotate(Node *y) {
        Node *x = y->right;
        Node *tmp = x->left;
        x->left = y;
        y->right = tmp;
        y->height = std::max(getHeight(y->left), getHeight(y->right)) + 1;
        x->height = std::max(getHeight(x->left), getHeight(x->right)) + 1;
        return x;
    }

左旋转

在这里插入图片描述
在这里插入图片描述

Node *rightRotate(Node *y) {
        Node *x = y->left;
        Node *tmp = x->right;
        x->right = y;
        y->left = tmp;
        y->height = std::max(getHeight(y->left), getHeight(y->right)) + 1;
        x->height = std::max(getHeight(x->left), getHeight(x->right)) + 1;
        return x;
    }

LL和RR

在这里插入图片描述

LR

在这里插入图片描述

RL

在这里插入图片描述
在这里插入图片描述

Github Code

#pragma once

#include <algorithm>

template<typename Key, typename Value>
class AVLTree {
private:
    class Node {
    public:
        Key key;
        Value value;
        Node *left;
        Node *right;
        int height;

        Node(Key key, Value value) : key(key), value(value), left(nullptr), right(nullptr), height(1) {}

        Node(Node *node) {
            this->key = node->key;
            this->value = node->value;
            this->left = node->left;
            this->right = node->right;
            this->height = node->height;
        }
    };

    Node *root;
    int size;

public:

    explicit AVLTree() : root(nullptr), size(0) {}

    ~AVLTree() noexcept {
        destroy(root);
    }

    constexpr int getSize() const noexcept {
        return size;
    }

    int isEmpty() const noexcept {
        return size == 0;
    }
    //返回节点高度
    int getHeight(const Node *const node) const noexcept {
        if (node == nullptr) {
            return 0;
        }
        return node->height;
    }
    //返回平衡因子
    int getBalanceFactor(const Node *const node) const noexcept {
        if (node == nullptr) {
            return 0;
        }
        return getHeight(node->left) - getHeight(node->right);
    }
    //判断是否是二叉搜索树
    bool isBST() {
        std::vector<Key> keys;
        inOrder(root, keys);
        for (int i = 1; i < keys.size(); ++i) {
            if (keys.at(i - 1) < keys.at(i)) {
                return false;
            }
        }
        return true;
    }
    //判断是否平衡
    bool isBalanced() const {
        return isBalanced(root);
    }

    void add(Key key, Value value) {
        root = add(root, key, value);
    }

    bool contains(Key key) {
        return getNode(root, key) != nullptr;
    }

    Value *get(Key key) {
        Node *node = getNode(root, key);
        return node == nullptr ? nullptr : &(node->value);
    }

    void set(Key key, Value newValue) {
        Node *node = getNode(root, key);
        if (node != nullptr) {
            node->value = newValue;
        }
    }

    // 从二叉树中删除键值为key的节点
    Value *remove(Key key) {
        Node *node = getNode(root, key);
        if (node != nullptr) {
            root = remove(root, key);
            return &(node->value);
        }
        return nullptr;
    }

private:

    // 向以node为根的二叉搜索树中,插入节点(key, value)
    // 返回插入新节点后的二叉搜索树的根
    Node *add(Node *node, Key key, Value value) {
        if (node == nullptr) {
            size++;
            return new Node(key, value);
        }
        if (key == node->key) {
            node->value = value;
        } else if (key < node->key) {
            node->left = add(node->left, key, value);
        } else {
            node->right = add(node->right, key, value);
        }
        node->height = 1 + std::max(getHeight(node->left), getHeight(node->right));
        int balanceFactor = getBalanceFactor(node);

        if (balanceFactor > 1 && getBalanceFactor(node->left) >= 0) {
            return rightRotate(node);
        }

        if (balanceFactor < -1 && getBalanceFactor(node->right) <= 0) {
            return leftRotate(node);
        }

        if (balanceFactor > 1 && getBalanceFactor(node->left) < 0) {
            node->left = leftRotate(node->left);
            return rightRotate(node);
        }

        if (balanceFactor < -1 && getBalanceFactor(node->right) > 0) {
            node->right = rightRotate(node->right);
            return leftRotate(node);
        }
        return node;
    }

    // 在以node为根的二叉搜索树中查找key所对应的Node
    Node *getNode(Node *node, Key key) {
        if (node == nullptr) {
            return nullptr;
        }
        if (key == node->key) {
            return node;
        } else if (key < node->key) {
            return getNode(node->left, key);
        } else {
            return getNode(node->right, key);
        }
    }

    void destroy(Node *node) {
        if (node != nullptr) {
            destroy(node->left);
            destroy(node->right);
            delete node;
            size--;
        }
    }

    // 在以node为根的二叉搜索树中,返回最小键值的节点
    Node *minimum(Node *node) {
        if (node->left == nullptr)
            return node;
        return minimum(node->left);
    }

    // 在以node为根的二叉搜索树中,返回最大键值的节点
    Node *maximum(Node *node) {
        if (node->right == nullptr)
            return node;
        return maximum(node->right);
    }

    // 删除掉以node为根的二分搜索树中的最小节点
    // 返回删除节点后新的二分搜索树的根
    Node *removeMin(Node *node) {
        if (node->left == nullptr) {
            Node *rightNode = node->right;
            delete node;
            size--;
            return rightNode;
        }

        node->left = removeMin(node->left);
        return node;
    }

    // 删除掉以node为根的二分搜索树中的最大节点
    // 返回删除节点后新的二分搜索树的根
    Node *removeMax(Node *node) {
        if (node->right == nullptr) {
            Node *leftNode = node->left;
            delete node;
            size--;
            return leftNode;
        }

        node->right = removeMax(node->right);
        return node;
    }

    // 删除掉以node为根的二分搜索树中键值为key的节点
    // 返回删除节点后新的二分搜索树的根
    Node *remove(Node *node, Key key) {
        if (node == nullptr) {
            return nullptr;
        }

        Node *retNode;
        if (key < node->key) {
            node->left = remove(node->left, key);
            retNode = node;
        } else if (key > node->key) {
            node->right = remove(node->right, key);
            retNode = node;
        } else {
            if (node->left == nullptr) {
                Node *rightNode = node->right;
                delete node;
                size--;
                retNode = rightNode;
            } else if (node->right == nullptr) {
                Node *leftNode = node->left;
                delete node;
                size--;
                retNode = leftNode;
            } else {
                Node *successor = new Node(minimum(node->right));
                size++;

                successor->right = remove(node->right, successor->key);
                successor->left = node->left;

                delete node;
                size--;

                retNode = successor;
            }
        }
        if (retNode == nullptr) {
            return nullptr;
        }
        retNode->height = 1 + std::max(getHeight(retNode->left), getHeight(retNode->right));
        int balanceFactor = getBalanceFactor(retNode);

        if (balanceFactor > 1 && getBalanceFactor(retNode->left) >= 0) {
            return rightRotate(retNode);
        }

        if (balanceFactor < -1 && getBalanceFactor(retNode->right) <= 0) {
            return leftRotate(retNode);
        }

        if (balanceFactor > 1 && getBalanceFactor(retNode->left) < 0) {
            retNode->left = leftRotate(retNode->left);
            return rightRotate(retNode);
        }

        if (balanceFactor < -1 && getBalanceFactor(retNode->right) > 0) {
            retNode->right = rightRotate(retNode->right);
            return leftRotate(retNode);
        }

        return retNode;
    }

    void inOrder(Node *node, std::vector<Key> keys) {
        if (node == nullptr) {
            return;
        }
        inOrder(node->left, keys);
        keys.push_back(node->key);
        inOrder(node->right, keys);
    }

    bool isBalanced(const Node *const node) const {
        if (node == nullptr) {
            return true;
        }
        int balanceFactor = getBalanceFactor(node);
        if (std::abs(balanceFactor) > 1) {
            return false;
        }

        return isBalanced(node->left) && isBalanced(node->right);
    }

    Node *leftRotate(Node *y) {
        Node *x = y->right;
        Node *tmp = x->left;
        x->left = y;
        y->right = tmp;
        y->height = std::max(getHeight(y->left), getHeight(y->right)) + 1;
        x->height = std::max(getHeight(x->left), getHeight(x->right)) + 1;
        return x;
    }

    Node *rightRotate(Node *y) {
        Node *x = y->left;
        Node *tmp = x->right;
        x->right = y;
        y->left = tmp;
        y->height = std::max(getHeight(y->left), getHeight(y->right)) + 1;
        x->height = std::max(getHeight(x->left), getHeight(x->right)) + 1;
        return x;
    }
};
AVL是一种自平衡的二叉查找,它确保了的高度始终保持在对数级别,从而保证了查找、插入和删除操作的时间复杂度为O(log n)。这种数据结构以它的发明者G.M. Adelson-Velsky和E.M. Landis的名字命名[^1]。 ### AVL的定义 - **平衡因子**:每个节点都有一个平衡因子,它是该节点左子的高度减去右子的高度。对于AVL来说,所有节点的平衡因子只能是-1, 0或1。 - **空**:如果T是空,则它自然是一个AVL。 - **非空**:若T不是空,则其左右子TL和TR都必须是AVL,并且对于任意节点,|HL - HR| ≤ 1(其中HL和HR分别表示左子和右子的高度)。 ### AVL的操作 当进行插入或删除操作时,可能会破坏AVL的平衡性,这时需要通过旋转来重新恢复平衡: - **单旋转**: - 左单旋(Single Rotate with Left)用于处理左孩子的左子过高。 - 右单旋(Single Rotate with Right)用于处理右孩子的右子过高。 - **双旋转**: - 左右双旋(Double Rotate with Left)用于处理左孩子的右子过高的情况。 - 右左双旋(Double Rotate with Right)用于处理右孩子的左子过高的情况。 这些旋转操作可以保持AVL的性质不变,并且能够快速地调整的结构以维持平衡。 ### AVL的实现 下面是一个简单的C语言代码示例,展示了如何定义AVL的节点以及实现基本的插入操作。这个例子中包含了必要的函数声明和一些核心逻辑。 ```c #include <stdio.h> #include <stdlib.h> // 定义AVL节点结构体 typedef struct AvlNode { int element; struct AvlNode *left; struct AvlNode *right; int height; // 节点的高度 } *AvlTree, *Position; // 获取两个整数中的较大者 int max(int a, int b) { return (a > b) ? a : b; } // 计算给定节点的高度 static int Height(AvlTree T) { if (T == NULL) return -1; // 空节点高度为-1 else return T->height; } // 单旋转 - 左左情况 AvlTree singlerotatewithLeft(AvlTree K2) { Position K1 = K2->left; K2->left = K1->right; K1->right = K2; // 更新节点高度 K2->height = max(Height(K2->left), Height(K2->right)) + 1; K1->height = max(Height(K1->left), K2->height) + 1; return K1; // 新根 } // 单旋转 - 右右情况 AvlTree singlerotatewithRight(AvlTree K2) { Position K1 = K2->right; K2->right = K1->left; K1->left = K2; // 更新节点高度 K2->height = max(Height(K2->left), Height(K2->right)) + 1; K1->height = max(Height(K1->right), K2->height) + 1; return K1; // 新根 } // 双旋转 - 左右情况 AvlTree doublerotatewithLeft(AvlTree K3) { K3->left = singlerotatewithRight(K3->left); return singlerotatewithLeft(K3); } // 双旋转 - 右左情况 AvlTree doublerotatewithRight(AvlTree K3) { K3->right = singlerotatewithLeft(K3->right); return singlerotatewithRight(K3); } // 插入新元素到AVLAvlTree Insert(int x, AvlTree T) { if (T == NULL) { // 如果为空,创建新节点 T = (AvlTree)malloc(sizeof(struct AvlNode)); if (T == NULL) printf("Out of space!!!"); else { T->element = x; T->left = T->right = NULL; T->height = 0; // 新叶节点高度为0 } } else if (x < T->element) { // 向左子插入 T->left = Insert(x, T->left); // 检查并修复平衡 if (Height(T->left) - Height(T->right) == 2) { if (x < T->left->element) T = singlerotatewithLeft(T); // 左左旋转 else T = doublerotatewithLeft(T); // 左右旋转 } } else if (x > T->element) { // 向右子插入 T->right = Insert(x, T->right); // 检查并修复平衡 if (Height(T->right) - Height(T->left) == 2) { if (x > T->right->element) T = singlerotatewithRight(T); // 右右旋转 else T = doublerotatewithRight(T); // 右左旋转 } } // 更新高度 T->height = max(Height(T->left), Height(T->right)) + 1; return T; } ``` 上述代码提供了AVL的基本框架,包括节点定义、插入操作及必要的旋转方法。实际应用中可能还需要添加更多的功能,如删除节点、查找特定值等。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

吃米饭

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

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

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

打赏作者

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

抵扣说明:

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

余额充值