二叉平衡搜索树(AVL树)

AVL树

二叉平衡搜索树 = BST树 + 节点平衡操作

节点平衡: 任意节点左右子树的高度差不超过1

节点旋转操作

AVL树为了维护节点平衡引入的四种节点旋转操作。

  • 右旋转
  • 左旋转
  • 先左旋再右旋
  • 先右旋再左旋

节点失衡的原因

  • 左孩子的左子树太高了,需要进行右旋操作
  • 右孩子的右子树太高了,需要进行左旋操作
  • 左孩子的右子树太高了,需要先进行左旋,再进行右旋 (左平衡操作)
  • 右孩子的左子树太高了,需要先进行右旋,再进行左旋(右平衡操作)

代码实现

节点平衡操作

cpp 复制代码
#include <iostream>
#include <cmath>
using namespace std;


// AVL树
template<typename T>
class AVLTree {
public:
private:
  // 定义AVL树节点类型
  struct Node {
    Node(T data = T())
      : data_(data)
      , left_(nullptr)
      , right_(nullptr)
      , height_(1)
    {}
    T data_;
    Node* left_;
    Node* right_;
    int height_;  // 记录节点的高度值
  };

  // 返回节点的高度值
  int height(Node* node) {
    return node == nullptr ? 0 : node->height_;
  }

  // 右旋转操作
  // 以参数node为轴做右旋转操作,并把新的根节点返回
  Node* rightRotate(Node* node) {

    // 旋转操作
    Node* child = node->left_;
    node->left_ = child->right_;
    child->right_ = node;

    // 高度更新
    node->height_ = max(height(node->left_), height(node->right_)) + 1;

    child->height_ = max(height(child->left_), height(child->right_)) + 1;

    return child;  // 返回旋转后的子树新的根节节点
  }

  // 左旋转操作
  // 以参数node为轴做左旋转操作,并把新的根节点返回
  Node* leftRotate(Node* node) {

    // 旋转操作
    Node* child = node->right_;
    node->right_ = child->left_;
    child->left_ = node;

    // 高度更新
    node->height_ = max(height(node->left_), height(node->right_)) + 1;

    child->height_ = max(height(child->left_), height(child->right_)) + 1;

    return child;  // 返回旋转后的子树新的根节节点
  }

  // 左平衡操作
  Node* leftBalance(Node* node) {
    node->left_ = leftRotate(node->left_);
    return rightRotate(node);
  }

  // 右平衡操作
  Node* rightBalance(Node* node) {
    node->right_ = rightRotate(node->right_);
    return leftRotate(node);
  }

  Node* root_; // 指向根节点

};

int main() {



  return 0;
}

插入操作

cpp 复制代码
#include <iostream>
#include <algorithm>
using namespace std;


// AVL树
template<typename T>
class AVLTree {
public:
  AVLTree():root_(nullptr) {}

  // AVL树的插入操作
  void insert(const T& val) {
    root_ = insert(root_, val);
  }
private:
  // 定义AVL树节点类型
  struct Node {
    Node(T data = T())
      : data_(data)
      , left_(nullptr)
      , right_(nullptr)
      , height_(1)
    {}
    T data_;
    Node* left_;
    Node* right_;
    int height_;  // 记录节点的高度值
  };

  // 返回节点的高度值
  int height(Node* node) {
    return node == nullptr ? 0 : node->height_;
  }

  // 右旋转操作
  // 以参数node为轴做右旋转操作,并把新的根节点返回
  Node* rightRotate(Node* node) {

    // 旋转操作
    Node* child = node->left_;
    node->left_ = child->right_;
    child->right_ = node;

    // 高度更新
    node->height_ = max(height(node->left_), height(node->right_)) + 1;

    child->height_ = max(height(child->left_), height(child->right_)) + 1;

    return child;  // 返回旋转后的子树新的根节节点
  }

  // 左旋转操作
  // 以参数node为轴做左旋转操作,并把新的根节点返回
  Node* leftRotate(Node* node) {

    // 旋转操作
    Node* child = node->right_;
    node->right_ = child->left_;
    child->left_ = node;

    // 高度更新
    node->height_ = max(height(node->left_), height(node->right_)) + 1;

    child->height_ = max(height(child->left_), height(child->right_)) + 1;

    return child;  // 返回旋转后的子树新的根节节点
  }

  // 左平衡操作
  Node* leftBalance(Node* node) {
    node->left_ = leftRotate(node->left_);
    return rightRotate(node);
  }

  // 右平衡操作
  Node* rightBalance(Node* node) {
    node->right_ = rightRotate(node->right_);
    return leftRotate(node);
  }

  // AVL树插入操作实现
  Node* insert(Node* node, const T &val) {
    if (node == nullptr) {  // 递归结束,找到插入的位置了
       return new Node(val);
    }

    if (node->data_ > val) {
      node->left_ = insert(node->left_, val);
      // 在递归回溯时候判断节点是否失衡

      if (height(node->left_) - height(node->right_) > 1) {  // node的左子树太高,node失衡了
        if (height(node->left_->left_) > height(node->left_->right_)) {  // 左孩子的左子树高度太高,节点失衡
          node = rightRotate(node);  // 更新下node
        } else {  // 左孩子的右子树太高了
          node = leftBalance(node);
        }
      }

    } else if (node->data_ < val) {
      node->right_ = insert(node->right_, val);

      // 在递归回溯时候判断节点是否失衡
      if (height(node->right_) - height(node->left_) > 1) {  // node的右子树太高,node失衡了
        if (height(node->right_->right_) > height(node->right_->left_)) {  // 右孩子的右子树高度太高,节点失衡
          node = leftRotate(node);  // 更新下node
        } else {  // 右孩子的左子树太高了
          node = rightBalance(node);
        }
      }
    } else {
       ; // 相等节点,不进行操作
    }

    // 因为子树中增加了新的节点,在递归回溯时检测更新节点高度
    node->height_ = max(height(node->left_), height(node->right_)) + 1;

    return node;
  }

  Node* root_; // 指向根节点

};

int main() {

  AVLTree<int> avlTree;

  for (int i = 1; i <= 10; i++) {
    avlTree.insert(i);
  }


  return 0;
}

测试

插入1 2 3 4 5 6 7 8 9 10这几个数字

删除操作

cpp 复制代码
#include <iostream>
#include <algorithm>
using namespace std;




// AVL树
template<typename T>
class AVLTree {
public:
  AVLTree():root_(nullptr) {}

  // AVL树的插入操作
  void insert(const T& val) {
    root_ = insert(root_, val);
  }

  // AVL树删除操作
  void remove(const T& val) {
    root_ = remove(root_, val);
  }
private:
  // 定义AVL树节点类型
  struct Node {
    Node(T data = T())
      : data_(data)
      , left_(nullptr)
      , right_(nullptr)
      , height_(1)
    {}
    T data_;
    Node* left_;
    Node* right_;
    int height_;  // 记录节点的高度值
  };

  // 返回节点的高度值
  int height(Node* node) {
    return node == nullptr ? 0 : node->height_;
  }

  // 右旋转操作
  // 以参数node为轴做右旋转操作,并把新的根节点返回
  Node* rightRotate(Node* node) {

    // 旋转操作
    Node* child = node->left_;
    node->left_ = child->right_;
    child->right_ = node;

    // 高度更新
    node->height_ = max(height(node->left_), height(node->right_)) + 1;

    child->height_ = max(height(child->left_), height(child->right_)) + 1;

    return child;  // 返回旋转后的子树新的根节节点
  }

  // 左旋转操作
  // 以参数node为轴做左旋转操作,并把新的根节点返回
  Node* leftRotate(Node* node) {

    // 旋转操作
    Node* child = node->right_;
    node->right_ = child->left_;
    child->left_ = node;

    // 高度更新
    node->height_ = max(height(node->left_), height(node->right_)) + 1;

    child->height_ = max(height(child->left_), height(child->right_)) + 1;

    return child;  // 返回旋转后的子树新的根节节点
  }

  // 左平衡操作
  Node* leftBalance(Node* node) {
    node->left_ = leftRotate(node->left_);
    return rightRotate(node);
  }

  // 右平衡操作
  Node* rightBalance(Node* node) {
    node->right_ = rightRotate(node->right_);
    return leftRotate(node);
  }

  // AVL树插入操作实现
  Node* insert(Node* node, const T &val) {
    if (node == nullptr) {  // 递归结束,找到插入的位置了
       return new Node(val);
    }

    if (node->data_ > val) {
      node->left_ = insert(node->left_, val);
      // 在递归回溯时候判断节点是否失衡

      if (height(node->left_) - height(node->right_) > 1) {  // node的左子树太高,node失衡了
        if (height(node->left_->left_) > height(node->left_->right_)) {  // 左孩子的左子树高度太高,节点失衡
          node = rightRotate(node);  // 更新下node
        } else {  // 左孩子的右子树太高了
          node = leftBalance(node);
        }
      }

    } else if (node->data_ < val) {
      node->right_ = insert(node->right_, val);

      // 在递归回溯时候判断节点是否失衡
      if (height(node->right_) - height(node->left_) > 1) {  // node的右子树太高,node失衡了
        if (height(node->right_->right_) > height(node->right_->left_)) {  // 右孩子的右子树高度太高,节点失衡
          node = leftRotate(node);  // 更新下node
        } else {  // 右孩子的左子树太高了
          node = rightBalance(node);
        }
      }
    } else {
       ; // 相等节点,不进行操作
    }

    // 因为子树中增加了新的节点,在递归回溯时检测更新节点高度
    node->height_ = max(height(node->left_), height(node->right_)) + 1;

    return node;
  }

  // AVL树删除操作实现
  Node* remove(Node* node, const T& val) {

    if (node == nullptr) {  // 没找到
      return nullptr;
    }

    if (node->data_ > val) {
      node->left_ = remove(node->left_, val);

      // 左子树删除节点,可能导致右子树太高了
      if (height(node->right_) - height(node->left_) > 1) {
        if (height(node->right_->right_) >= height(node->right_->left_)) {
          // 右孩子的右子树太高了,做左旋转操作
          node = leftRotate(node);
        } else {
          // 右孩子的左子树太高
          node = rightBalance(node);
        }
      }

    } else if (node->data_ < val) {
      node->right_ = remove(node->right_, val);

      // 右子树删除节点,可能导致左子树太高了
      if (height(node->left_) - height(node->right_) > 1) {
        if (height(node->left_->left_) >= height(node->left_->right_)) {
          // 左孩子的左子树太高了,做右旋转操作
          node = rightRotate(node);
        } else {
          // 左孩子的右子树太高
          node = leftBalance(node);
        }
      }

    } else {
      // 找到了
      // 先处理有两个孩子的节点删除情况
      if (node->left_ != nullptr && node->right_ != nullptr) {
        // 为了避免删除前驱或者后继节点造成节点失衡谁高删除谁
        if (height(node->left_) >= height(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* left = node->left_;
          delete node;
          return left;
        } else if (node->right_ != nullptr) {
          Node* right = node->right_;
          delete node;
          return right;
        } else {
          return nullptr;
        }
      }
    }

    // 更新节点高度
    node->height_ = max(height(node->left_), height(node->right_)) + 1;

    return node;  // 递归回溯过程中,把当前节点返回给父节点

  }

  Node* root_; // 指向根节点

};

int main() {

  AVLTree<int> avlTree;

  for (int i = 1; i <= 10; i++) {
    avlTree.insert(i);
  }

  avlTree.remove(6);


  return 0;
}

测试

删除8

相关推荐
飒飒真编程1 小时前
C++类模板继承部分知识及测试代码
开发语言·c++·算法
GeminiGlory1 小时前
算法练习6-大数乘法(高精度乘法)
算法
熬了夜的程序员1 小时前
【华为机试】HJ61 放苹果
算法·华为·面试·golang
马特说1 小时前
基于随机森林的金融时间序列预测系统:从数据处理到实时预测的完整流水线
算法·随机森林·金融
呆呆的小鳄鱼1 小时前
leetcode:HJ18 识别有效的IP地址和掩码并进行分类统计[华为机考][字符串]
算法·leetcode·华为
艾莉丝努力练剑2 小时前
【C语言】学习过程教训与经验杂谈:思想准备、知识回顾(五)
c语言·开发语言·数据结构·学习·算法
freexyn2 小时前
Matlab自学笔记六十二:求解三角函数方程的通解周期解
笔记·算法·matlab
zstar-_2 小时前
【算法笔记】7.LeetCode-Hot100-图论专项
笔记·算法·leetcode
xienda2 小时前
冒泡、选择、插入排序:三大基础排序算法深度解析(C语言实现)
数据结构·算法·排序算法
用户40315986396632 小时前
带 WriteBuffer 的内存读写操作
java·算法