文章目录
一、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实现
插入节点后需要注意检验当前节点时候平衡,并进行相应的旋转操作
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_;
};