完整教程:【高阶数据结构】AVL树

1. 什么是 AVL 树?

简单来说,AVL 树是一种自平衡的二叉搜索树(BST)。AVL 树又叫做平衡二叉树。

它是由两位苏联数学家 G. M. Adelson-Velsky 和 E. M. Landis 在 1962 年发明的,AVL 这个名字就来源于他们姓氏的首字母。

核心思想:在二叉搜索树的基础上,增加了一个平衡条件——对于树中的任意一个节点,其左子树和右子树的高度差不能超过 1。

这个“高度差”有一个专门的名字,叫做 平衡因子。

平衡因子 = 右子树高度 - 左子树高度
平衡因子的值只能是 -1, 0, 1 中的一个。如果出现 2 或 -2,就意味着树不平衡了,需要通过“旋转”来修复。

2. 为什么需要 AVL 树?

普通的二叉搜索树(BST)有一个致命缺点:它的性能严重依赖于插入的顺序。

最佳情况:插入顺序得当,树是完全平衡的,搜索、插入、删除的时间复杂度都是 O(log n)。

最坏情况:如果插入的数据是有序的(例如 1, 2, 3, 4, 5…),BST 会退化成一条链表,时间复杂度变为 O(n)。

AVL 树就是为了解决这个问题而生的。它通过强制维持树的平衡,保证了在最坏情况下,所有操作的时间复杂度仍然是 O(log n)。这个“维持平衡”的过程,就是我们下面要讲的 旋转。

3. AVL树的模拟实现

3.1 AVL树的定义

#pragma once
#include <iostream>
  #include <assert.h>
    using namespace std;
    template<typename K, typename V>
      struct AVLTreeNode
      {
      pair<K, V> _kv;
        AVLTreeNode<K, V>* _left;
          AVLTreeNode<K, V>* _right;
            AVLTreeNode<K, V>* _parent;
              int _bf;
              AVLTreeNode(const pair<K, V>& kv)
                :_kv(kv)
                ,_left(nullptr)
                ,_right(nullptr)
                ,_parent(nullptr)
                ,_bf(0)
                {}
                };
                template<typename K, typename V>
                  class AVLTree
                  {
                  typedef AVLTreeNode<K, V> Node;
                    public:
                    AVLTree()
                    :_root(nullptr)
                    {}
                    private:
                    Node* _root;
                    };

3.2 AVL树的插入

关键步骤:

  1. 找到节点的位置
  2. 生成一个节点,并把节点连接好
  3. 以此节点的位置开始更新
      1. 把当前的parent节点平衡因子更新一下(+1或-1)
      1. 若parent节点平衡因子为0,则该树已经平衡,插入已经完成
      1. 若parent节点平衡因子为-1或者1,则继续更迭parent,cur,向上更新
      1. 若parent节点平衡因子为-2或者2,需要进行旋转,一次旋转(无论哪个旋转),都可以让parent的平衡因子直接变成0,即完成了插入。
bool Insert(const pair<K, V>& kv)
  {
  if (_root == nullptr)
  {
  _root = new Node(kv);
  return true;
  }
  else
  {
  Node* parent = nullptr;
  Node* cur = _root;
  //1. 找到节点的位置
  while (cur)
  {
  if (kv.first > cur->_kv.first)
  {
  parent = cur;
  cur = cur->_right;
  }
  else if (kv.first < cur->_kv.first)
    {
    parent = cur;
    cur = cur->_left;
    }
    else
    {
    return false;
    }
    }
    //2. 生成一个节点,并把节点连接好
    cur = new Node(kv);
    cur->_parent = parent;//三叉链需要额外链接cur的父亲节点
    if (cur->_kv.first > parent->_kv.first)
    {
    parent->_right = cur;
    }
    else
    {
    parent->_left = cur;
    }
    //3. 以此节点的位置开始更新
    while (parent)//当parent == nullptr的时候,无法继续向上更新,退出即可
    {
    //更新parent的平衡因子
    if (cur->_kv.first > parent->_kv.first)
    {
    parent->_bf++;
    }
    else
    {
    parent->_bf--;
    }
    if (parent->_bf == 0)//此时父亲节点的平衡因子为0,退出即可
    {
    break;
    }
    else if (parent->_bf == 1 || parent->_bf == -1)//迭代cur和parent,继续
    {											   //向上更新
    cur = parent;
    parent = parent->_parent;
    }
    else if (parent->_bf == 2 || parent->_bf == -2)//这里需要进行旋转
    {											   //怎么判断是什么情况的旋转
    if (parent->_bf == 2 && cur->_bf == 1)     //以及如何旋转请看后文
    {
    RotateL(parent);
    }
    else if (parent->_bf == -2 && cur->_bf == -1)
    {
    RotateR(parent);
    }
    else if (parent->_bf == 2 && cur->_bf == -1)
    {
    RotateRL(parent);
    }
    else if (parent->_bf == -2 && cur->_bf == 1)
    {
    RotateLR(parent);
    }
    else//如果cur的平衡因子不符合为1或-1的情况,说明在插入前树已经不是AVL树了
    {   //直接断言错误即可
    assert(false);
    }
    break;
    }
    else//如果parent的平衡因子不符合为0,1或-1,2或-2的情况
    {   //说明在插入前树已经不是AVL树了,直接断言错误即可
    assert(false);
    }
    }
    }
    return true;
    }

接下来,我们讲旋转,讲旋转前,要知道的事情。
当 parent 和 cur 的平衡因子确定时,他们的位置也就确定了。
我们讲的左单璇,parent->_bf == 2 && cur->_bf == 1,即(2,1)的情况。那么简洁操作图解一定是:

A (平衡因子=2) parent
\
B (平衡因子=1) cur
\
C (新插入)

不可能是:

A (平衡因子=2) parent
/
B (平衡因子=1)  cur
/
C (新插入)

因为parent平衡因子为2,那么右子树一定出现了问题,那么cur一定出现在右边,因为cur是从插入点一步步更新过来的。

3.2.1 情况 1:右右情况(RR)(2,1) -> 左单旋

parent的平衡因子为 2 ,cur的平衡因子为 1 时,是需要使用左单旋的情况。
左单璇后,parent、cur 的平衡因子都变成 0
在这里插入图片描述
代码解析:

void RotateL(Node* parent)
{
ode* ppnode = parent->_parent;
ode* cur = parent->_right;
Node* curleft = cur->_left;
parent->_right = curleft;
if (curleft)
{
curleft->_parent = parent;
}
cur->_left = parent;
parent->_parent = cur;
if (ppnode == nullptr)
{
_root = cur;
cur->_parent = nullptr;
}
else
{
cur->_parent = ppnode;
if (ppnode->_left == parent)
{
ppnode->_left = cur;
}
else
{
ppnode->_right = cur;
}
}
parent->_bf = cur->_bf = 0;
}

3.2.2 情况 2:左左情况(LL)(-2,-1) -> 右单旋

parent的平衡因子为 -2 ,cur的平衡因子为 -1 时,是需要使用右单旋的情况。
右单璇后,parent、cur 的平衡因子都变成 0
在这里插入图片描述
代码解析:

void RotateR(Node* parent)
{
Node* ppnode = parent->_parent;
Node* cur = parent->_left;
Node* curright = cur->_right;
parent->_left = curright;
if (curright)
{
curright->_parent = parent;
}
cur->_right = parent;
parent->_parent = cur;
if (ppnode == nullptr)
{
_root = cur;
cur->_parent = nullptr;
}
else
{
if (ppnode->_left == parent)
{
ppnode->_left = cur;
cur->_parent = ppnode;
}
else
{
ppnode->_right = cur;
cur->_parent = ppnode;
}
}
parent->_bf = cur->_bf = 0;
}

3.2.3 情况 3:右左情况(RL)(2,-1) -> 右左双旋(先右旋再左旋)

parent的平衡因子为 2 ,cur的平衡因子为 -1 时,是需要使用右左单旋的情况。
右左单璇后,parent、cur 的平衡因子不像左旋和右璇一样都变成0。而是有不同情况。

在这里插入图片描述
总结:

旋转前的curleft平衡因子旋转后的parent平衡因子旋转后的cur平衡因子旋转后的curleft平衡因子
第一种情况1-100
第二种情况-1100
第三种情况0000

代码如下:

void RotateRL(Node* parent)
{
Node* cur = parent->_right;
Node* curleft = cur->_left;
int bf = curleft->_bf;
RotateR(cur);
RotateL(parent);
if (bf == 0)//此时对应h == 0的情况,尽管我们调用的左单旋和右单旋中已经对parent和cur
{           //和curleft的平衡因子进行了无脑置0,但是这里为了解耦,即降低耦合性,所以
parent->_bf = 0;//我们需要手动置0,降低耦合性
cur->_bf = 0;
curleft->_bf = 0;
}
else if (bf == -1)
{
parent->_bf = 0;
cur->_bf = 1;
curleft->_bf = 0;
}
else if (bf == 1)
{
parent->_bf = -1;
cur->_bf = 0;
curleft->_bf = 0;
}
else//如果bf出现其它情况,那么插入前该树已经不是AVL树,所以直接断言false即可
{
assert(false);
}
}

3.2.4 情况 4:左右情况(LR)(-2,1) -> 左右双旋(先左旋再右旋)

parent的平衡因子为 -2 ,cur的平衡因子为 1 时,是需要使用左右单旋的情况。
左右单璇后,parent、cur 的平衡因子不像左旋和右璇一样都变成0。而是有不同情况。
其实就是和右左双旋同理。
在这里插入图片描述

总结:

旋转前的curright平衡因子旋转后的parent平衡因子旋转后的cur平衡因子旋转后的curright平衡因子
第一种情况10-10
第二种情况-1100
第三种情况0000

代码如下:

void RotateLR(Node* parent)
{
Node* cur = parent->_left;
Node* curright = cur->_right;
int bf = curright->_bf;
RotateL(cur);
RotateR(parent);
if (bf == 0)
{
parent->_bf = 0;
cur->_bf = 0;
curright->_bf = 0;
}
else if (bf == -1)
{
parent->_bf = 1;
cur->_bf = 0;
curright->_bf = 0;
}
else if (bf == 1)
{
parent->_bf = 0;
cur->_bf = -1;
curright->_bf = 0;
}
else
{
assert(false);
}
}

3.2.5 旋转的性质

无论是左单旋、右单旋,还是左右双旋、右左双旋,旋转后,都是二叉搜索树。
左右双旋、右左双旋其实就是2个单旋的组合。因为左单旋、右单旋不会改变树为二叉搜索树。所以,双旋也不会改变

性质:二叉搜索树前序为升序。
即无论怎么旋转,都没有改变其前序的位置。

不信可以看看左旋、右旋的例子:

在这里插入图片描述
前序一直是:a 30 b 60 c
在这里插入图片描述
前序一直是:a 30 b 60 c

3.3 AVL树的求高以及平衡的判断

代码如下:

int Height()
{
return Height(_root);
}
int Height(Node* root)
{
if (root == nullptr)
{
return 0;
}
int heightL = Height(root->_left);
int heightR = Height(root->_right);
return  heightL > heightR ? heightL + 1 : heightR + 1;
}
bool Isbalance()
{
return Isbalance(_root);
}
bool Isbalance(Node* root)
{
if (root == nullptr)
{
return true;
}
Node* left = root->_left;
Node* right = root->_right;
int heightL = Height(left);
int heightR = Height(right);
int bf = heightR - heightL;
if (bf != root->_bf)
{
cout << "平衡因子异常" << root->_kv.first << ':' << root->_bf << endl;
  return false;
  }
  return abs(bf) <= 1 && Isbalance(left) && Isbalance(right);
  }

4. 源代码

AVLTree.h:

#pragma once
#include <iostream>
  #include <assert.h>
    using namespace std;
    template<typename K, typename V>
      struct AVLTreeNode
      {
      pair<K, V> _kv;
        AVLTreeNode<K, V>* _left;
          AVLTreeNode<K, V>* _right;
            AVLTreeNode<K, V>* _parent;
              int _bf;
              AVLTreeNode(const pair<K, V>& kv)
                :_kv(kv)
                ,_left(nullptr)
                ,_right(nullptr)
                ,_parent(nullptr)
                ,_bf(0)
                {}
                };
                template<typename K, typename V>
                  class AVLTree
                  {
                  typedef AVLTreeNode<K, V> Node;
                    public:
                    AVLTree()
                    :_root(nullptr)
                    {}
                    bool Insert(const pair<K, V>& kv)
                      {
                      if (_root == nullptr)
                      {
                      _root = new Node(kv);
                      return true;
                      }
                      else
                      {
                      Node* parent = nullptr;
                      Node* cur = _root;
                      while (cur)
                      {
                      if (kv.first > cur->_kv.first)
                      {
                      parent = cur;
                      cur = cur->_right;
                      }
                      else if (kv.first < cur->_kv.first)
                        {
                        parent = cur;
                        cur = cur->_left;
                        }
                        else
                        {
                        return false;
                        }
                        }
                        cur = new Node(kv);
                        cur->_parent = parent;
                        if (cur->_kv.first > parent->_kv.first)
                        {
                        parent->_right = cur;
                        }
                        else
                        {
                        parent->_left = cur;
                        }
                        while (parent)
                        {
                        if (cur->_kv.first > parent->_kv.first)
                        {
                        parent->_bf++;
                        }
                        else
                        {
                        parent->_bf--;
                        }
                        if (parent->_bf == 0)
                        {
                        break;
                        }
                        else if (parent->_bf == 1 || parent->_bf == -1)
                        {
                        cur = parent;
                        parent = parent->_parent;
                        }
                        else if (parent->_bf == 2 || parent->_bf == -2)
                        {
                        if (parent->_bf == 2 && cur->_bf == 1)
                        {
                        RotateL(parent);
                        }
                        else if (parent->_bf == -2 && cur->_bf == -1)
                        {
                        RotateR(parent);
                        }
                        else if (parent->_bf == 2 && cur->_bf == -1)
                        {
                        RotateRL(parent);
                        }
                        else if (parent->_bf == -2 && cur->_bf == 1)
                        {
                        RotateLR(parent);
                        }
                        else
                        {
                        assert(false);
                        }
                        break;
                        }
                        else
                        {
                        assert(false);
                        }
                        }
                        }
                        return true;
                        }
                        int Height()
                        {
                        return Height(_root);
                        }
                        int Height(Node* root)
                        {
                        if (root == nullptr)
                        {
                        return 0;
                        }
                        int heightL = Height(root->_left);
                        int heightR = Height(root->_right);
                        return  heightL > heightR ? heightL + 1 : heightR + 1;
                        }
                        bool Isbalance()
                        {
                        return Isbalance(_root);
                        }
                        bool Isbalance(Node* root)
                        {
                        if (root == nullptr)
                        {
                        return true;
                        }
                        Node* left = root->_left;
                        Node* right = root->_right;
                        int heightL = Height(left);
                        int heightR = Height(right);
                        int bf = heightR - heightL;
                        if (bf != root->_bf)
                        {
                        cout << "平衡因子异常" << root->_kv.first << ':' << root->_bf << endl;
                          return false;
                          }
                          return abs(bf) <= 1 && Isbalance(left) && Isbalance(right);
                          }
                          private:
                          void RotateL(Node* parent)
                          {
                          Node* ppnode = parent->_parent;
                          Node* cur = parent->_right;
                          Node* curleft = cur->_left;
                          parent->_right = curleft;
                          if (curleft)
                          {
                          curleft->_parent = parent;
                          }
                          cur->_left = parent;
                          parent->_parent = cur;
                          if (ppnode == nullptr)
                          {
                          _root = cur;
                          cur->_parent = nullptr;
                          }
                          else
                          {
                          cur->_parent = ppnode;
                          if (ppnode->_left == parent)
                          {
                          ppnode->_left = cur;
                          }
                          else
                          {
                          ppnode->_right = cur;
                          }
                          }
                          parent->_bf = cur->_bf = 0;
                          }
                          void RotateR(Node* parent)
                          {
                          Node* ppnode = parent->_parent;
                          Node* cur = parent->_left;
                          Node* curright = cur->_right;
                          parent->_left = curright;
                          if (curright)
                          {
                          curright->_parent = parent;
                          }
                          cur->_right = parent;
                          parent->_parent = cur;
                          if (ppnode == nullptr)
                          {
                          _root = cur;
                          cur->_parent = nullptr;
                          }
                          else
                          {
                          if (ppnode->_left == parent)
                          {
                          ppnode->_left = cur;
                          cur->_parent = ppnode;
                          }
                          else
                          {
                          ppnode->_right = cur;
                          cur->_parent = ppnode;
                          }
                          }
                          parent->_bf = cur->_bf = 0;
                          }
                          void RotateRL(Node* parent)
                          {
                          Node* cur = parent->_right;
                          Node* curleft = cur->_left;
                          int bf = curleft->_bf;
                          RotateR(cur);
                          RotateL(parent);
                          if (bf == 0)
                          {
                          parent->_bf = 0;
                          cur->_bf = 0;
                          curleft->_bf = 0;
                          }
                          else if (bf == -1)
                          {
                          parent->_bf = 0;
                          cur->_bf = 1;
                          curleft->_bf = 0;
                          }
                          else if (bf == 1)
                          {
                          parent->_bf = -1;
                          cur->_bf = 0;
                          curleft->_bf = 0;
                          }
                          else
                          {
                          assert(false);
                          }
                          }
                          void RotateLR(Node* parent)
                          {
                          Node* cur = parent->_left;
                          Node* curright = cur->_right;
                          int bf = curright->_bf;
                          RotateL(cur);
                          RotateR(parent);
                          if (bf == 0)
                          {
                          parent->_bf = 0;
                          cur->_bf = 0;
                          curright->_bf = 0;
                          }
                          else if (bf == -1)
                          {
                          parent->_bf = 1;
                          cur->_bf = 0;
                          curright->_bf = 0;
                          }
                          else if (bf == 1)
                          {
                          parent->_bf = 0;
                          cur->_bf = -1;
                          curright->_bf = 0;
                          }
                          else
                          {
                          assert(false);
                          }
                          }
                          private:
                          Node* _root;
                          };
posted @ 2025-12-03 21:24  yangykaifa  阅读(0)  评论(0)    收藏  举报