AVL树特点:在BST树的基础上,引入了节点“平衡”的概念,任意一个节点的左右子树高度差不超过1。
为什么要引入平衡这个概念?
在BST树中,由于插入后就是排序好的,那就会存在这样一种情况:如果插入的元素依次增大,就会导致理想中的二叉树,形成了一个链表,如下图,这种情况会导致增删查的时间复杂度并不是O(logn),而是O(n)。为了解决这样情况,引入了平衡的概念。
template<typename T>
class AVLTree
{
private:
// 定义AVL树节点类型
struct Node
{
Node(T data = T())
: val_(data)
, left_(nullptr)
, right(nullptr)
, height_(1)
{}
T val_;
Node* left_;
Node* right_;
int height_; // 记录节点高度值
};
Node* root_; // 指向根节点
};
操作步骤:right rotate
代码:
// 返回节点的高度值
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_ = std::max(height(node->left_), height(node->right_)) + 1;
child->height_ = std::max(height(child->left_), height(child->right_)) + 1;
// 返回旋转后的子树新的根节点
return child;
}
操作步骤:left rotate
代码:
// 左旋转操作
Node* leftRotate(Node* node)
{
Node* child = node->right_;
node->right_ = child->left_;
child->left_ = node;
// 高度更新
node->height_ = std::max(height(node->left_), height(node->right_)) + 1;
child->height_ = std::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);
}
先来看看BST树的递归插入操作
如果node为nullptr,表示找到插入的位置了;
如果当前节点的值等于data,直接返回;
如果当前节点的值小于data,则递归往左子树走;
如果当前节点的值大于data,则递归往右子树走。
// 递归插入操作
Node* insert(Node* node, const T& data)
{
if (node == nullptr)
{
// 递归结束,找到插入data的位置,生成新节点返回
return new Node(data);
}
if (node->val_ == data)
{
return node;
}
else if (node->val_ < data))
{
node->right_ = insert(node->right_, data);
}
else
{
node->left_ = insert(node->left_, data);
}
return node;
}
AVL树的插入操作:相对于BST树增加了哪些内容?
如果node为nullptr,表示找到插入的位置了,递归结束,申请新节点返回;
// 插入操作实现
Node* insert(Node* node, const T& data)
{
if (node == nullptr) // 递归结束
{
return new Node(data);
}
if (node->val_ > data)
{
node->left_ = insert(node->left_, data);
// 添加1 在递归回溯时判断节点是否失衡
// node的左子树太高,左子树失衡
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->val_ < data)
{
node->right_ = insert(node->right_, data);
// 添加2 在递归回溯时判断节点是否失衡
// node的右子树太高,右子树失衡
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
{
; // 找到相同节点了,直接返回
}
// 添加3 添加节点后,回溯过程中检测更新节点高度
node->height_ = max(height(node->left_), height(node->right_)) + 1;
return node;
}
// 递归删除
Node* remove(Node* node, const T& data)
{
if (node == nullptr)
return nullptr;
if (node->val_ > data)
{
node->left_ = remove(node->left_, data);
}
else if (node->val_ < data)
{
node->right_ = remove(node->right_, data);
}
else // 找到待删除节点
{
// 情况3
if (node->left_ != nullptr && node->right_ != nullptr)
{
// 找前驱节点
Node* pre = node->left_;
while (pre->right_ != nullptr)
{
pre = pre->right_;
}
// 拿前驱节点值覆盖当前节点
node->val_ = pre->val_;
// 通过递归直接删除前驱节点
node->left_ = remove(node->left_, pre->val_);
}
else // 情况1和2
{
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 // 删除的是没有孩子的节点,叶子节点
{
delete node;
return nullptr; // 回溯时更新其父节点地址为nullptr
}
}
}
return node; // 把当前节点返回给父节点
}
因为要保证节点的度的平衡,所以增加了判断左右子树的高度以及旋转操作。
// 删除操作实现
Node* remove(Node* node, const T& data)
{
if (node == nullptr)
{
return nullptr;
}
if (node->val_ > data)
{
node->left_ = remove(node->left_, data);
// 左子树删除节点可能导致右子树太高
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->val_ < data)
{
node->right_ = remove(node->right_, data);
// 右子树删除节点,可能导致左子树太高
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->val_ = pre->val_;
node->left_ = remove(node->left_, pre->val_); // 删前驱节点
}
else
{
// 删后继
Node* post = node->right_;
while (post->left_ != nullptr)
{
post = post->left_;
}
node->val_ = post->val_;
node->right_ = remove(node->right_, post->val_); // 删后继节点
}
}
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; // 回溯过程中,把当前节点给父节点返回
}
AVL树构造完成后:
删除元素6后的结果:这里体现的是删除的第三三种情况,待删除节点有两个孩子
再删除元素8后:这里体现的还是第三种情况,由于左右子树高度一样,所有删前驱节点去了。。
再删除元素7之后:也是第三种情况,有两个孩子,这时候右子树高,所以递归删除右子树。
AVL树较难,写这篇文章时却没有画太多的图,所以如果有看不懂得地方可以多想几遍,然后画图,在心里面记住几个旋转情况。
完整代码:
#include
#include
using namespace std;
// AVL树
template<typename T>
class AVLTree
{
public:
AVLTree() : root_(nullptr) {}
// AVL树的插入操作
void insert(const T& data)
{
root_ = insert(root_, data);
}
// AVL树的删除操作
void remove(const T& data)
{
root_ = remove(root_, data);
}
private:
// 定义AVL树节点类型
struct Node
{
Node(T data = T())
: val_(data)
, left_(nullptr)
, right_(nullptr)
, height_(1)
{}
T val_;
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_ = std::max(height(node->left_), height(node->right_)) + 1;
child->height_ = std::max(height(child->left_), height(child->right_)) + 1;
// 返回旋转后的子树新的根节点
return child;
}
// 左旋转操作
Node* leftRotate(Node* node)
{
Node* child = node->right_;
node->right_ = child->left_;
child->left_ = node;
// 高度更新
node->height_ = std::max(height(node->left_), height(node->right_)) + 1;
child->height_ = std::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* insert(Node* node, const T& data)
{
if (node == nullptr) // 递归结束
{
return new Node(data);
}
if (node->val_ > data)
{
node->left_ = insert(node->left_, data);
// 添加1 在递归回溯时判断节点是否失衡
// node的左子树太高,左子树失衡
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->val_ < data)
{
node->right_ = insert(node->right_, data);
// 添加2 在递归回溯时判断节点是否失衡
// node的右子树太高,右子树失衡
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
{
; // 找到相同节点了,直接返回
}
// 添加3 添加节点后,回溯过程中检测更新节点高度
node->height_ = max(height(node->left_), height(node->right_)) + 1;
return node;
}
// 删除操作实现
Node* remove(Node* node, const T& data)
{
if (node == nullptr)
{
return nullptr;
}
if (node->val_ > data)
{
node->left_ = remove(node->left_, data);
// 左子树删除节点可能导致右子树太高
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->val_ < data)
{
node->right_ = remove(node->right_, data);
// 右子树删除节点,可能导致左子树太高
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->val_ = pre->val_;
node->left_ = remove(node->left_, pre->val_); // 删前驱节点
}
else
{
// 删后继
Node* post = node->right_;
while (post->left_ != nullptr)
{
post = post->left_;
}
node->val_ = post->val_;
node->right_ = remove(node->right_, post->val_); // 删后继节点
}
}
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(void)
{
AVLTree<int> avl;
for (int i = 1; i <= 10; ++i)
{
avl.insert(i);
}
avl.remove(6);
avl.remove(8);
avl.remove(7);
return 0;
}