1. AVL树的概念
二叉搜索树虽然可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,导致查找元素相当于在顺序表中搜索元素,效率低下。
为了解决这个问题,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis发明了一种方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1,即可降低树的高度,从而减少平均搜索长度,提高效率。
空树和具有以下性质的二叉搜索树是AVL树。
- 左右子树也是AVL树。
- 左右子树高度之差的绝对值不超过1。
比如,左边的树是AVL树,右边的树不是。
2. 结点的定义
由于会频繁计算左右子树的高度,对于结点的定义,多加一个成员_bf表示左右子树高度差。称为平衡因子。之前的二叉搜索树,存储的是key结构,这里存储的是<key,value>的键值对的结构。插入操作会引发旋转,需要找到父节点,所以增加一个父指针,指向父结点。
结构如下
template<class K, class V>
struct AVLTreeNode
{
AVLTreeNode(const pair<K, V>& kv)
:_kv(kv),
_left(nullptr),
_right(nullptr),
_parent(nullptr),
_bf(0)
{}
pair<K, V> _kv;
AVLTreeNode<K, V>* _left;
AVLTreeNode<K, V>* _right;
AVLTreeNode<K, V>* _parent;
int _bf; // 右子树高度 - 左子树高度
};
3. 插入
类里面只有一个指针指向根节点。
typedef AVLTreeNode<K, V> Node;
Node* _root = nullptr;
AVL树是二叉搜索树,插入结点步骤:
1.先找插入位置,插入并链接。
2.更新平衡因子。如果出现不符合AVL树性质的情况,需要进行旋转调整。
总结:
- 更新后平衡因子为0,说明更新前为-1或1,插入的结点在较低的那一侧,不用继续向上更新。
- 更新后平衡因子为-1/1,说明更新前为0,需要继续向上调整
- 更新后平衡因子为-2/2,不用继续向上调整,但是破坏了AVL树的规则,需要调整。
部分代码如下。
bool Insert(const pair<K, V>& kv)
{
if (_root == nullptr) //空树
{
_root = new Node(kv);
return true;
}
Node* cur = _root;
Node* parent = _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;
}
}
//new结点并链接
cur = new Node(kv);
if (kv.first > parent->_kv.first)
parent->_right = cur;
else
parent->_left = cur;
cur->_parent = parent;
while (parent)
{
//更新平衡因子
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 = cur->_parent;
}
else if (parent->_bf == -2 || parent->_bf == 2)
{
//不符合AVL树标准,旋转调整
//....
}
else
assert(false);
}
return true;
}
旋转的逻辑。不同高度的AVL树和插入的情况可以有多种情况的组合,但是可以抽象为下面的样子。
情况一:
上图为左子树高于右子树的情况,进行右单旋。如果右子树高于左子树,同理需要进行左单旋。旋转逻辑类似。旋转时,需要考虑60结点的情况,它可能是根结点,可能是某节点的子树。
void RotateLeft(Node* parent) //左单旋
{
Node* Pparent = parent->_parent;
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
subR->_left = parent;
parent->_parent = subR;
subR->_parent = Pparent;
if (subRL)
subRL->_parent = parent;
if (Pparent == nullptr) //说明parent结点为原来的根
_root = subR;
else
{
if (Pparent->_left == parent)
Pparent->_left = subR;
else
Pparent->_right = subR;
}
parent->_bf = subR->_bf = 0;
}
void RotateRight(Node* parent) //右单旋
{
Node* Pparent = parent->_parent;
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
subL->_right = parent;
parent->_parent = subL;
subL->_parent = Pparent;
if (subLR)
subLR->_parent = parent;
if (Pparent == nullptr) //调整点为根结点
_root = subL;
else
{
if (Pparent->_left == parent)
Pparent->_left = subL;
else
Pparent->_right = subL;
}
//更新平衡因子
parent->_bf = subL->_bf = 0;
}
情况二:
总结:
- 新节点插入较高左子树的左侧—左左:右单旋
- 新节点插入较高右子树的右侧—右右:左单旋
- 新节点插入较高左子树的右侧—左右:先左单旋再右单旋
- 新节点插入较高右子树的左侧—右左:先右单旋再左单旋
前面两者为一边高,后两者为一高一低。(对于整棵树和插入结点所在的子树而言)
双旋最后的注意点为平衡因子的更新。
void RotateRightLeft(Node* parent) //右左双旋
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
int bf = subRL->_bf;
RotateRight(parent->_right);
RotateLeft(parent);
if (bf == 0)
{
subR->_bf = 0;
subRL->_bf = 0;
parent->_bf = 0;
}
else if (bf == -1)
{
subR->_bf = 1;
subRL->_bf = 0;
parent->_bf = 0;
}
else if (bf == 1)
{
subR->_bf = 0;
subRL->_bf = 0;
parent->_bf = -1;
}
else
assert(false);
}
void RotateLeftRight(Node* parent) //左右双旋
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
int bf = subLR->_bf;
RotateLeft(parent->_left);
RotateRight(parent);
if (bf == 0)
{
subL->_bf = 0;
subLR->_bf = 0;
parent->_bf = 0;
}
else if (bf == -1)
{
subL->_bf = 0;
subLR->_bf = 0;
parent->_bf = 1;
}
else if (bf == 1)
{
subL->_bf = -1;
subLR->_bf = 0;
parent->_bf = 0;
}
else
assert(false);
}
4. 判断平衡二叉树
先计算左右子树的高度差,判断是否为1/-1/0。由于结点内的成员添加了_bf,所以还需要判断二者是否相等,最后递归左右子树。
//计算树的高度
int _Height(Node* root)
{
if (root == nullptr)
return 0;
int left = _Height(root->_left);
int right = _Height(root->_right);
return left > right ? left + 1 : right + 1;
}
bool _IsBalanceTree(Node* root)
{
if (root == nullptr)
return true;
int leftHeight = _Height(root->_left);
int rightHeight = _Height(root->_right);
int diff = rightHeight - leftHeight;
if (abs(diff) > 1 || root->_bf != diff)
return false;
return _IsBalanceTree(root->_left) && _IsBalanceTree(root->_right);
}
测试程序
5. 其他接口
find的实现比较容易,比较key即可。
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;
}
构造,拷贝构造,赋值和析构函数。
拷贝构造,先拷贝根结点,在拷贝左子树,在拷贝右子树,需要注意父节点指针的指向。
AVLTree(const AVLTree& a)
{
_root = Copy(a._root);
}
Node* Copy(Node* root)
{
if (root == nullptr)
return nullptr;
Node* newNode = new Node(root->_kv);
newNode->_left = Copy(root->_left); //拷贝左子树
if (newNode->_left) //链接父亲
newNode->_left->_parent = newNode;
newNode->_right = Copy(root->_right); //拷贝右子树
if (newNode->_right) 链接父亲
newNode->_right->_parent = newNode;
return newNode;
}
因为我们显示写了拷贝构造函数,编译器就不会生成默认构造了,也就不能空初始化了,可以使用default关键字强制编译器生成默认构造。
AVLTree() = default;
赋值,交换两个对象的指针即可。
Node* operator=(AVLTree a)
{
swap(_root, a._root);
return _root;
}
析构函数,后序遍历平衡搜索树,进行是否资源
~AVLTree()
{
Destroy(_root);
_root = nullptr;
}
void Destroy(Node* root)
{
if (root == nullptr)
return;
Destroy(root->_left);
Destroy(root->_right);
delete root;
root = nullptr;
}
6. 完整代码
AVLTree.h
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
template<class K, class V>
struct AVLTreeNode
{
AVLTreeNode(const pair<K, V>& kv)
:_kv(kv),
_left(nullptr),
_right(nullptr),
_parent(nullptr),
_bf(0)
{}
pair<K, V> _kv;
AVLTreeNode<K, V>* _left;
AVLTreeNode<K, V>* _right;
AVLTreeNode<K, V>* _parent;
int _bf; // 右子树高度 - 左子树高度
};
template<class K, class V>
class AVLTree
{
typedef AVLTreeNode<K, V> Node;
public:
AVLTree() = default;
AVLTree(const AVLTree& a)
{
_root = Copy(a._root);
}
Node* operator=(AVLTree a)
{
swap(_root, a._root);
return _root;
}
bool Insert(const pair<K, V>& kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
return true;
}
Node* cur = _root;
Node* parent = _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;
}
}
//new结点并链接
cur = new Node(kv);
if (kv.first > parent->_kv.first)
parent->_right = cur;
else
parent->_left = cur;
cur->_parent = parent;
while (parent)
{
//链接父节点
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 = cur->_parent;
}
else if (parent->_bf == -2 || parent->_bf == 2)
{
//不符合AVL树标准,旋转调整
if (parent->_bf == 2 && cur->_bf == 1) //左旋
RotateLeft(parent);
else if (parent->_bf == -2 && cur->_bf == -1) //右旋
RotateRight(parent);
else if (parent->_bf == 2 && cur->_bf == -1) //右左双旋
RotateRightLeft(parent);
else if (parent->_bf == -2 && cur->_bf == 1) //左右双旋
RotateLeftRight(parent);
break;
}
else
assert(false);
}
return true;
}
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;
}
void Inorder()
{
_Inorder(_root);
cout << endl;
}
bool IsBalanceTree()
{
return _IsBalanceTree(_root);
}
int Height()
{
return _Height(_root);
}
~AVLTree()
{
Destroy(_root);
_root = nullptr;
}
private:
Node* Copy(Node* root)
{
if (root == nullptr)
return nullptr;
Node* newNode = new Node(root->_kv);
newNode->_left = Copy(root->_left); //拷贝左子树
if (newNode->_left) //链接父亲
newNode->_left->_parent = newNode;
newNode->_right = Copy(root->_right); //拷贝右子树
if (newNode->_right) 链接父亲
newNode->_right->_parent = newNode;
return newNode;
}
void Destroy(Node* root)
{
if (root == nullptr)
return;
Destroy(root->_left);
Destroy(root->_right);
delete root;
root = nullptr;
}
int _Height(Node* root)
{
if (root == nullptr)
return 0;
int left = _Height(root->_left);
int right = _Height(root->_right);
return left > right ? left + 1 : right + 1;
}
bool _IsBalanceTree(Node* root)
{
if (root == nullptr)
return true;
int leftHeight = _Height(root->_left);
int rightHeight = _Height(root->_right);
int diff = rightHeight - leftHeight;
if (abs(diff) > 1 || root->_bf != diff)
return false;
return _IsBalanceTree(root->_left) && _IsBalanceTree(root->_right);
}
void RotateLeft(Node* parent) //左单旋
{
Node* Pparent = parent->_parent;
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
subR->_left = parent;
parent->_parent = subR;
subR->_parent = Pparent;
if (subRL)
subRL->_parent = parent;
if (Pparent == nullptr) //说明parent结点为原来的根
_root = subR;
else
{
if (Pparent->_left == parent)
Pparent->_left = subR;
else
Pparent->_right = subR;
}
parent->_bf = subR->_bf = 0;
}
void RotateRight(Node* parent) //右单旋
{
Node* Pparent = parent->_parent;
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
subL->_right = parent;
parent->_parent = subL;
subL->_parent = Pparent;
if (subLR)
subLR->_parent = parent;
if (Pparent == nullptr) //调整点为根结点
_root = subL;
else
{
if (Pparent->_left == parent)
Pparent->_left = subL;
else
Pparent->_right = subL;
}
//更新平衡因子
parent->_bf = subL->_bf = 0;
}
void RotateRightLeft(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
int bf = subRL->_bf;
RotateRight(parent->_right);
RotateLeft(parent);
if (bf == 0)
{
subR->_bf = 0;
subRL->_bf = 0;
parent->_bf = 0;
}
else if (bf == -1)
{
subR->_bf = 1;
subRL->_bf = 0;
parent->_bf = 0;
}
else if (bf == 1)
{
subR->_bf = 0;
subRL->_bf = 0;
parent->_bf = -1;
}
else
assert(false);
}
void RotateLeftRight(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
int bf = subLR->_bf;
RotateLeft(parent->_left);
RotateRight(parent);
if (bf == 0)
{
subL->_bf = 0;
subLR->_bf = 0;
parent->_bf = 0;
}
else if (bf == -1)
{
subL->_bf = 0;
subLR->_bf = 0;
parent->_bf = 1;
}
else if (bf == 1)
{
subL->_bf = -1;
subLR->_bf = 0;
parent->_bf = 0;
}
else
assert(false);
}
void _Inorder(Node* root)
{
if (root == nullptr)
return;
_Inorder(root->_left);
cout << root->_kv.first << ":" << root->_kv.second << endl;
_Inorder(root->_right);
}
private:
Node* _root = nullptr;
};
test.cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include"AVLTree.h"
void testAVLTree()
{
AVLTree<int, int> avl;
//int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
int b[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
for (auto val : b)
{
avl.Insert({ val,val });
}
avl.Inorder();
AVLTree<int, int> a(avl);
a.Insert({8,8});
a.Inorder();
AVLTree<int, int> c;
c = a;
c.Inorder();
cout << "树的高度" << avl.Height() << endl;
}
void testAVLTree1()
{
AVLTree<int, int> avl;
int b[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
for (auto& e : b)
{
avl.Insert({ e,e });
}
avl.Inorder();
if (avl.IsBalanceTree()) cout << "是平衡搜索树" << endl;
else cout << "不是平衡搜索树" << endl;
}
int main()
{
testAVLTree();
//testAVLTree1();
return 0;
}