平衡二叉树(AVL树)

0 阅读2分钟

基础

为什么需要AVL树?

先回顾BST的问题,如果插入数据是有序的,比如1 → 2 → 3 → 4 → 5

BST会退化成:

1
 \
  2
   \
    3
     \
      4
       \
        5

此时,查找复杂度从O(logN)退化到O(N)。

AVL树的目标:始终保持“平衡”的BST。(也就是说:任意节点的左右子树高度差"平衡因子"<=1)

保持平衡的方法就是“旋转”,当插入/删除后,|平衡因子| > 1,就说明树部平衡了,需要旋转恢复。

四种失衡类型

LL(左左)

    30
   /
  20
 /
10

解决方式:右旋

RR(右右)

10
  \
   20
     \
      30

解决方式:左旋

LR(左右)

    30
   /
  10
    \
     20

解决方式:先左旋再右旋

RL(右左)

10
  \
   30
  /
 20

解决方式:先右旋再左旋

实现

#include <algorithm> //max

struct TreeNode
{
    int val;
    int height;
    TreeNode* left;
    TreeNode* right;

    TreeNode(int v) : val(v), height(1), left(nullptr), right(nullptr) {}

};

int getHeight(TreeNode* node)
{
    return node ? node->height : 0;
}
int getBalance(TreeNode* node)
{
    return node ? getHeight(node->left) - getHeight(node->right) : 0;
}

//左旋
TreeNode* leftRotate(TreeNode* x)
{
    //传来的参数是失衡节点
    //左旋:将当前节点的右孩子提升为新的根节点,当前节点成为其左子节点,
    //同时将右孩子原本的左子树调整为当前节点的右子树
    TreeNode* y = x->right;
    TreeNode* T2 = y->left;

    y->left = x;
    x->right = T2;

    //更新高度
    x->height = std::max(getHeight(x->left), getHeight(x->right));
    y->height = std::max(getHeight(y->left), getHeight(y->right));

    return y;
}

//右旋
TreeNode* rightRotate(TreeNode* y)
{
    TreeNode* x = y->left;
    TreeNode* T2 = x->right;

    x->right = y;
    y->left = T2;

    x->height = std::max(getHeight(x->left), getHeight(x->right));
    y->height = std::max(getHeight(y->left), getHeight(y->right));

    return x;
}

TreeNode* insert(TreeNode* root, int val)
{
    //1.BST插入
    if(root == nullptr) return new TreeNode(val);
    if (val > root->val) root->right = insert(root->right, val);
    else if (val < root->val) root->left = insert(root->left, val);
    else return root;
    //2.更新高度
    root->height = std::max(getHeight(root->left), getHeight(root->right)) + 1;
    //3.计算平衡因子
    int balance = getBalance(root);
    //4.旋转(四种情况)
    //LL
    if (balance > 1 && val < root->left->val)   //当平衡因子大于1,说明左子树过高;如果插入值小于左子节点的值,说明插入发生在左子树的左侧,属于LL型失衡,需要通过一次右旋来恢复平衡 
        return rightRotate(root);
    //RR
    if (balance < -1 && val > root->right->val)
        return leftRotate(root);
    //LR
    if (balance > 1 && val > root->left->val)
    {
        root->left = leftRotate(root->left);
        return rightRotate(root);
    }
    //RL
    if (balance < -1 && val < root->right->val)
    {
        root->right = rightRotate(root->right);
        return leftRotate(root);
    }
    return root;
}