目录
一、AVL树的定义
1.1AVL树的定义
AVL树,又称为平衡二叉树它基于二叉搜索树并通过平衡而得到。二叉搜索树可以提高搜索数据的效率,但在数据有序的情况下会退化为单支树,此时在树中查找元素就得遍历一整个分支,时间复杂度也会退化至O(N)。但是使用平衡二叉树的话,可以使二叉搜索树时刻保持左右子树的平衡,就可以避免这种最坏情况。
1.2AVL树节点的定义
我们可以在节点中定义一个平衡因子,如果左子树比右子树高一层,那么平衡因子就为-1;如果左右子树一样高,平衡因子就为0;如果右子树比左子树高一层,那么平衡因子就为1,这三种情况下AVL树的性质都没有被打破。
按照这个规则,如果平衡因子为-2、2或其他值,则说明左右子树已经失衡,性质被打破。
在调整失衡的AVL树时,我们需要频繁的访问父节点,所以在AVL树中我们需要使用三叉链,因此AVL树的节点除了包含左右子节点的指针,还需要一个指向父节点的指针。所以这里介绍下树节点的定义,代码描述如下:
template<class K,class V>
struct AVLTreeNode
{
AVLTreeNode<K, V>* _left;
AVLTreeNode<K, V>* _right;
AVLTreeNode<K, V>* _parent;
pair<K, V> _kv; //第一个数据存储key,第二个数据存储value
int _bf; //平衡因子(balance factor)
AVLTreeNode(const pair<const K, V>& kv)
:_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_kv(kv)
,_bf(0) //新节点左右都为空,平衡因子为0
{}
};
二、AVL树的性质
当我们向二叉搜索树中插入新节点时,如果能用某种方法时刻保证树中每个节点的左右子树高度之差不超过1,就可以降低整棵树的高度,保证每条分支的平衡
AVL树的性质如下:
- AVL树可以是空树
- 一颗AVL树的左右子树都是AVL树
- 一颗AVL树的左右子树高度差不超过1
三、AVL树的插入
向AVL树中插入节点与向二叉搜索树中插入节点的过程基本相同,唯一的区别就是AVL树在插入节点后可能存在失衡的情况,需要调整。
我们先按照二叉搜索树的规则将节点插入到AVL树中,并判断插入的节点在父节点的左边还是右;按照平衡因子的规则,如果新节点插入到了父节点的左侧,那么父节点的平衡因子-1,如果新节点插入到了父节点的右侧,那么父节点的平衡因子+1;以上,便是新增节点的父节点平衡因子可能的变化情况。
但是!插入一个节点不但会影响父节点,还可能会影响到祖先节点。
我们观察上面的四种可能,其中左边的两种情况下,插入节点后以父节点为根的子树高度发生了变化;在右边的两种情况下,插入节点后以父节点为根的子树高度没有发生变化。
观察过后可以发现,当父节点的平衡因子从0变为1/-1后,子树高度发生变化;当父节点的平衡因子从1/-1变为0后,子树高度不发生变化
如果以父节点为根的子树高度没有发生变化,那么就不会影响到祖先节点的平衡因子;如果高度变了就会继续向上影响到祖先节点的平衡因子
因此,我们可以通过判断节点的插入位置来计算父节点的平衡因子,进而判断子树高度是否发生变化,再进一步计算对祖先节点平衡因子的影响,来判断AVL树是否失衡。
至此,我们已经可以开始写插入新节点和更新平衡因子的代码了:
template<class K, class V>
class AVLTree
{
typedef AVLTreeNode<K, V> Node;
public:
bool insert(const pair<const K, V>& kv)
{
if (_root == nullptr) //检测为空树的情况
{
_root = new Node(kv);
return true;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur) //搜索新节点的插入位置
{
parent = cur;
if (kv.first > cur->_kv.first)
cur = cur->_right;
else if (kv.first < cur->_kv.first)
cur = cur->_left;
else
return false;
}
cur = new Node(kv);
//将父节点与新节点链接
//比较新节点和父节点的key判断插入到左边还是右边
if (kv.first > parent->_kv.first) //这里防止有人看不懂再强调一遍,kv是pair类型的对象,kv.first是key,kv.second是value
{
parent->_right = cur;
cur->_parent = parent;
}
else
{
parent->_left = cur;
cur->_parent = parent;
}
while (cur != _root)
{
//插入节点后除了对父节点造成影响还可能对祖宗节点造成影响
//因此随着循环进行,这里的cur不一定为新节点,可以理解为高度发生变化的子树的根节点
//更新父节点的平衡因子
if (cur == parent->_left)
parent->_bf--;
else
parent->_bf++;
//更新后检测父节点的平衡因子
if (parent->_bf == 0) //平衡因子为0说明没有打破性质,跳出循环
break;
else if (parent->_bf == 1 || parent->_bf == -1) //更新后平衡因子为1或-1说明高度发生变化,改变cur和parent的指向后继续向上更新
{
cur = parent;
parent = parent->_parent;
}
else if (parent->_bf == 2 || parent->_bf == -2) //更新后平衡因子为2或-2.说明已经失衡,需要调整
{
//不同情况的调整方法...
if (parent->_bf == 2)
{
if (cur->_bf == 1)
{
//...
}
else if (cur->_bf == -1)
{
//...
}
}
else
{
if (cur->_bf == 1)
{
//...
}
else if (cur->_bf == -1)
{
//...
}
}
break;
}
else //平衡因子出现意外情况,报错
{
assert(false);
}
}
return true;
}
private:
Node* _root = nullptr;
};
四、AVL树的平衡调整
如果在一颗原本平衡的AVL树中插入一个新节点,可能会造成失衡,此时需要调整树的结构使之重新平衡,这种调整方法称为旋转。
根据AVL树的原本结构和节点插入位置的不同分为四种情况和四种旋转方式:
(1)新节点插入较高左子树的左侧:右单旋
(2)新节点插入较高右子树的右侧:左单旋
(3)新节点插入较高左子树的右侧:先左单旋再右单旋(左右双旋)
(4)新节点插入较高右子树的左侧:先右单旋再左单旋(右左双旋)
由这四种情况可知,代码实现如下:
#include <iostream>
using namespace std;
// 定义AVL树的节点结构
struct Node {
int key;
Node* left;
Node* right;
int height;
Node(int k) : key(k), left(NULL), right(NULL), height(1) {}
};
// 获取节点的高度
int height(Node* node) {
if (node == NULL)
return 0;
return node->height;
}
// 获取两个数中的最大值
int max(int a, int b) {
return (a > b)? a : b;
}
// 右旋转操作
Node* rightRotate(Node* y) {
Node* x = y->left;
Node* T2 = x->right;
// 执行旋转
x->right = y;
y->left = T2;
// 更新高度
y->height = max(height(y->left), height(y->right)) + 1;
x->height = max(height(x->left), height(x->right)) + 1;
return x;
}
// 左旋转操作
Node* leftRotate(Node* x) {
Node* y = x->right;
Node* T2 = y->left;
// 执行旋转
y->left = x;
x->right = T2;
// 更新高度
x->height = max(height(x->left), height(x->right)) + 1;
y->height = max(height(y->left), height(y->right)) + 1;
return y;
}
// 获取平衡因子
int getBalance(Node* node) {
if (node == NULL)
return 0;
return height(node->left) - height(node->right);
}
// 插入节点操作
Node* insert(Node* node, int key) {
// 正常的BST插入操作
if (node == NULL)
return new Node(key);
if (key < node->key)
node->left = insert(node->left, key);
else if (key > node->key)
node->right = insert(node->right, key);
else // 不允许插入相同的键值
return node;
// 更新节点的高度
node->height = 1 + max(height(node->left), height(node->right));
// 获取平衡因子
int balance = getBalance(node);
// 左左情况
if (balance > 1 && key < node->left->key)
return rightRotate(node);
// 右右情况
if (balance < -1 && key > node->right->key)
return leftRotate(node);
// 左右情况
if (balance > 1 && key > node->left->key) {
node->left = leftRotate(node->left);
return rightRotate(node);
}
// 右左情况
if (balance < -1 && key < node->right->key) {
node->right = rightRotate(node->right);
return leftRotate(node);
}
return node;
}
// 前序遍历
void preOrder(Node* root) {
if (root!= NULL) {
cout << root->key << " ";
preOrder(root->left);
preOrder(root->right);
}
}
int main() {
Node* root = NULL;
root = insert(root, 10);
root = insert(root, 20);
root = insert(root, 30);
root = insert(root, 40);
root = insert(root, 50);
root = insert(root, 25);
cout << "Preorder traversal of the constructed AVL tree is: ";
preOrder(root);
return 0;
}
五、AVL树的验证
5.1验证二叉搜索树的稳定性
最重要的插入节点部分完成了,不过在验证是否符合AVL树性质前,我们首先需要验证其是否是一棵二叉搜索树
在之前讲解二叉搜索树中提到过,如果中序遍历能够得到一个有序的序列,就说明是二叉搜索树
void InOrder()
{
_InOrder(_root);
cout << endl;
}
void _InOrder(Node *root)
{
if (root == nullptr)
return;
_InOrder(root->_left);
cout << root->_kv.first << " "; // key/value模型,我们只打印key即可
_InOrder(root->_right);
}
由程序:
int main()
{
int a[] = { 16,3,7,11,9,26,18,14,15 };
AVLTree<int, int> t;
for (auto i : a)
{
t.Insert(make_pair(i, i));
}
t.InOrder();
return 0;
}
得出结果:3,7,9,11,14,15,16,18,26;说明符合二叉搜索树的性质。
5.2 验证二叉搜索树的平衡性
要验证是否符合AVL树性质,只需要检测它的所有节点的子树高度差不超过1即可
需要注意的是,这里不可以直接通过判断平衡因子的绝对值是否大于1来验证平衡,因为平衡因子是不客观的,可以被修改
因此,我们通过递归来得到每棵子树的高度并进行判断即可
代码如下:
#include <iostream>
using namespace std;
// 定义 AVL 树的节点结构
struct Node {
int key;
Node* left;
Node* right;
int height;
Node(int k) : key(k), left(NULL), right(NULL), height(1) {}
};
// 获取节点的高度
int height(Node* node) {
if (node == NULL)
return 0;
return node->height;
}
// 获取两个数中的最大值
int max(int a, int b) {
return (a > b)? a : b;
}
// 右旋转操作
Node* rightRotate(Node* y) {
Node* x = y->left;
Node* T2 = x->right;
// 执行旋转
x->right = y;
y->left = T2;
// 更新高度
y->height = max(height(y->left), height(y->right)) + 1;
x->height = max(height(x->left), height(x->right)) + 1;
return x;
}
// 左旋转操作
Node* leftRotate(Node* x) {
Node* y = x->right;
Node* T2 = y->left;
// 执行旋转
y->left = x;
x->right = T2;
// 更新高度
x->height = max(height(x->left), height(x->right)) + 1;
y->height = max(height(y->left), height(y->right)) + 1;
return y;
}
// 获取平衡因子
int getBalance(Node* node) {
if (node == NULL)
return 0;
return height(node->left) - height(node->right);
}
// 插入节点操作
Node* insert(Node* node, int key) {
if (node == NULL)
return new Node(key);
if (key < node->key)
node->left = insert(node->left, key);
else if (key > node->key)
node->right = insert(node->right, key);
else // 不允许插入相同的键值
return node;
// 更新节点的高度
node->height = 1 + max(height(node->left), height(node->right));
// 获取平衡因子
int balance = getBalance(node);
// 左左情况
if (balance > 1 && key < node->left->key)
return rightRotate(node);
// 右右情况
if (balance < -1 && key > node->right->key)
return leftRotate(node);
// 左右情况
if (balance > 1 && key > node->left->key) {
node->left = leftRotate(node->left);
return rightRotate(node);
}
// 右左情况
if (balance < -1 && key < node->right->key) {
node->right = rightRotate(node->right);
return leftRotate(node);
}
return node;
}
// 检查 AVL 树是否平衡的函数
bool isBalanced(Node* node) {
if (node == NULL)
return true;
// 获取平衡因子
int balance = getBalance(node);
// 平衡因子的绝对值大于 1 则不平衡
if (abs(balance) > 1)
return false;
// 递归检查左右子树是否平衡
return isBalanced(node->left) && isBalanced(node->right);
}
int main() {
Node* root = NULL;
root = insert(root, 10);
root = insert(root, 20);
root = insert(root, 30);
root = insert(root, 40);
root = insert(root, 50);
root = insert(root, 25);
// 检查 AVL 树是否平衡
if (isBalanced(root)) {
cout << "The AVL tree is balanced." << endl;
} else {
cout << "The AVL tree is not balanced." << endl;
}
return 0;
}
六、AVL树的删除
AVL树的删除可以使用二叉搜索树的方式,先用二叉搜索树的方式来对目标节点进行删除,再判断是否失衡,如果失衡则进入旋转操作,旋转后得到的新AVL树就是删除后的AVL树。
代码实现如下:
#include <iostream>
using namespace std;
// 定义 AVL 树的节点结构
struct Node {
int key;
Node* left;
Node* right;
int height;
Node(int k) : key(k), left(NULL), right(NULL), height(1) {}
};
// 获取节点的高度
int height(Node* node) {
if (node == NULL)
return 0;
return node->height;
}
// 获取两个数中的最大值
int max(int a, int b) {
return (a > b)? a : b;
}
// 右旋转操作
Node* rightRotate(Node* y) {
Node* x = y->left;
Node* T2 = x->right;
// 执行旋转
x->right = y;
y->left = T2;
// 更新高度
y->height = max(height(y->left), height(y->right)) + 1;
x->height = max(height(x->left), height(x->right)) + 1;
return x;
}
// 左旋转操作
Node* leftRotate(Node* x) {
Node* y = x->right;
Node* T2 = y->left;
// 执行旋转
y->left = x;
x->right = T2;
// 更新高度
x->height = max(height(x->left), height(x->right)) + 1;
y->height = max(height(y->left), height(y->right)) + 1;
return y;
}
// 获取平衡因子
int getBalance(Node* node) {
if (node == NULL)
return 0;
return height(node->left) - height(node->right);
}
// 辅助函数:获取最小键值节点
Node* minValueNode(Node* node) {
Node* current = node;
while (current->left!= NULL)
current = current->left;
return current;
}
// 删除节点操作
Node* deleteNode(Node* root, int key) {
// 标准的 BST 删除操作
if (root == NULL)
return root;
if (key < root->key)
root->left = deleteNode(root->left, key);
else if (key > root->key)
root->right = deleteNode(root->right, key);
else {
// 节点只有一个子节点或没有子节点
if ((root->left == NULL) || (root->right == NULL)) {
Node* temp = root->left? root->left : root->right;
if (temp == NULL) {
temp = root;
root = NULL;
} else
*root = *temp;
delete temp;
} else {
// 节点有两个子节点
Node* temp = minValueNode(root->right);
root->key = temp->key;
root->right = deleteNode(root->right, temp->key);
}
}
// 如果树只有一个节点
if (root == NULL)
return root;
// 更新节点的高度
root->height = 1 + max(height(root->left), height(root->right));
// 获取平衡因子
int balance = getBalance(root);
// 左左情况
if (balance > 1 && getBalance(root->left) >= 0)
return rightRotate(root);
// 左右情况
if (balance > 1 && getBalance(root->left) < 0) {
root->left = leftRotate(root->left);
return rightRotate(root);
}
// 右右情况
if (balance < -1 && getBalance(root->right) <= 0)
return leftRotate(root);
// 右左情况
if (balance < -1 && getBalance(root->right) > 0) {
root->right = rightRotate(root->right);
return leftRotate(root);
}
return root;
}
// 前序遍历
void preOrder(Node* root) {
if (root!= NULL) {
cout << root->key << " ";
preOrder(root->left);
preOrder(root->right);
}
}
int main() {
Node* root = NULL;
root = deleteNode(root, 10);
root = deleteNode(root, 20);
root = deleteNode(root, 30);
root = deleteNode(root, 40);
root = deleteNode(root, 50);
root = deleteNode(root, 25);
cout << "Preorder traversal of the constructed AVL tree is: ";
preOrder(root);
return 0;
}
七、AVL树的性能及代码
AVL树追求的是严格平衡,因此可以保证查找时高效的时间复杂度O(logN),但是如果我们需要频繁的对其进行旋转来维护平衡,一定程度上会影响效率,尤其是删除节点时的最差情况下我们可能需要一路旋转到根的位置。
AVL树的完整代码如下:
template<class K,class V>
struct AVLTreeNode
{
AVLTreeNode<K, V>* _left;
AVLTreeNode<K, V>* _right;
AVLTreeNode<K, V>* _parent;
pair<K, V> _kv;
int _bf; //平衡因子
AVLTreeNode(const pair<const K, V>& kv)
:_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_kv(kv)
,_bf(0)
{}
};
template<class K, class V>
class AVLTree
{
typedef AVLTreeNode<K, V> Node;
public:
bool insert(const pair<const K, V>& kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
return true;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
parent = cur;
if (kv.first > cur->_kv.first)
cur = cur->_right;
else if (kv.first < cur->_kv.first)
cur = cur->_left;
else
return false;
}
cur = new Node(kv);
if (kv.first > parent->_kv.first)
{
parent->_right = cur;
cur->_parent = parent;
}
else
{
parent->_left = cur;
cur->_parent = parent;
}
while (cur != _root)
{
if (cur == parent->_left)
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)
{
if (cur->_bf == 1)
{
RotateLeft(parent);
}
else if (cur->_bf == -1)
{
RotateRL(parent);
}
}
else
{
if (cur->_bf == 1)
{
RotateLR(parent);
}
else if (cur->_bf == -1)
{
RotateRight(parent);
}
}
break;
}
else
{
assert(false);
}
}
return true;
}
void RotateLeft(Node* parent) //新节点插入较高右子树的右侧:左单旋
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if(subRL)
subRL->_parent = parent;
Node* parentParent = parent->_parent;
if (parent != _root)
{
subR->_parent = parentParent;
if (parent == parentParent->_left)
parentParent->_left = subR;
else
parentParent->_right = subR;
}
else
{
_root = subR;
subR->_parent = nullptr;
}
subR->_left = parent;
parent->_parent = subR;
parent->_bf = subR->_bf = 0;
}
void RotateRight(Node* parent) //新节点插入较高左子树的左侧:右单旋
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
Node* parentParent = parent->_parent;
if (parent != _root)
{
subL->_parent = parentParent;
if (parent == parentParent->_left)
parentParent->_left = subL;
else
parentParent->_right = subL;
}
else
{
_root = subL;
subL->_parent = nullptr;
}
subL->_right = parent;
parent->_parent = subL;
parent->_bf = subL->_bf = 0;
}
void RotateRL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
int bf = subRL->_bf;
RotateRight(subR);
RotateLeft(parent);
if (bf == 0)
{
parent->_bf = subR->_bf = subRL->_bf = 0;
}
else if (bf == 1)
{
parent->_bf = -1;
subR->_bf = subRL->_bf = 0;
}
else if (bf == -1)
{
subR->_bf = 1;
parent->_bf = subRL->_bf = 0;
}
else
{
assert(false);
}
}
void RotateLR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
int bf = subLR->_bf;
RotateLeft(subL);
RotateRight(parent);
if (bf == 0)
{
parent->_bf = subL->_bf = subLR->_bf = 0;
}
else if (bf == 1)
{
subL->_bf = -1;
parent->_bf = subLR->_bf = 0;
}
else if (bf == -1)
{
parent->_bf = 1;
subL->_bf = subLR->_bf = 0;
}
else
{
assert(false);
}
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
bool IsBalance()
{
return _IsBalance(_root);
}
int Height()
{
return _Height(_root);
}
size_t Size()
{
return _Size(_root);
}
Node* Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (key > cur->_kv.first)
cur = cur->_right;
else if (key < cur->_kv.first)
cur = cur->_left;
else
return cur;
}
return nullptr;
}
private:
void _InOrder(Node* root)
{
if (root == nullptr)
return;
_InOrder(root->_left);
cout << root->_kv.first << " ";
_InOrder(root->_right);
}
bool _IsBalance(Node* root)
{
if (root == nullptr)
return true;
int leftHeigit = _Height(root->_left);
int rightHeight = _Height(root->_right);
if (rightHeight - leftHeigit != root->_bf)
{
cout << root->_kv.first << "平衡因子异常" << endl;
return false;
}
return abs(rightHeight - leftHeigit) <= 1
&& _IsBalance(root->_left)
&& _IsBalance(root->_right);
}
int _Height(Node* root)
{
if (root == nullptr)
return 0;
int higher = max(_Height(root->_left), _Height(root->_right));
return higher + 1;
}
size_t _Size(Node* root)
{
if (root == nullptr)
return 0;
return _Size(root->_left) + _Size(root->_right) + 1;
}
private:
Node* _root = nullptr;
};