文章目录
一、AVL tree 的概述
AVL Tree 是一个“加上了额外的平衡条件的二叉搜索树”。其平衡条件的建立是为了确保整棵树的深度为O(logN)。直观上的最佳平衡条件是每个节点的左右子树有相同的高度,但这太严苛,我们很难插入新元素而又保持这样的平衡条件。AVL Tree 于是退而求其次,要求任何节点的左右子树高度相差最多1。这是一个较弱的条件,但仍能够保证“对数深度”平衡状态。
如下图所示的是一个AVL Tree,插入了节点11之后,灰色节点违法了AVL Tree的平衡条件。由于只有“插入点至根节点”路径上的各节点可能改变平衡状态,因此,只要调整其中最深的那个节点,便可使整棵树重获平衡。
前面说过,只要调整“插入点至根节点”路径上,平衡状态被破坏之各节点中最深的那一个,便可以使整棵树重新获得平衡。假设该最深节点为X,由于节点最多拥有两个节点,而所谓“平衡被破坏”意味着X的左右两颗子树的高度相差2,因此我们可以轻易将情况分为四种。
- 插入点位于X的左子节点的左子树——左左
- 插入点位于X的左子节点的右子树——左右
- 插入点位于X的右子节点的左子树——右左
- 插入点位于X的右子节点的右子树——右右
二、AVL Tree的平衡操作
1.单旋转
在外侧插入状态中,k2“插入前平衡,插入后不平衡”的唯一情况如下图左侧所示:
为了调整平衡状态,我们希望将A子树提高一层,并将C子树下降一层,如上图右侧所示。我们可以这样想象,把k1向上提起,使k2自然下滑,并将B子树挂到k2的左侧。这么做是因为,二叉搜索树的规则使我们知道,k2>k1,所以k2必须成为新树形中的k1的右子节点。二叉搜索树的规则也告诉我们,B子树的所有节点的键值都在k1和k2之间,所以新树形中的B子树必须落在k2的左侧。
2.双旋转
下图左侧为内侧插入所造成的不平衡状态。单旋转无法解决这种情况。第一,我们不能再以k3为根节点,其次,我们不能将k3和k1做一次单旋转,因为旋转后还是不平衡。唯一的可能是以k2为新的根节点,这使得k1必须成为k2的左子节点,k3必须成为k2的右子节点,而这么一来也就完全决定了四个子树的位置。新的树形满足AVL Tree的平衡条件,并且,就像单旋转的情况一样,它恢复了节点插入之前的高度,因此不需要任何调整。
下图是两次单旋转的过程:
三、AVL Tree的代码实现
1.节点结构
这里我们引入了平衡因子的概念,平衡因子 = 右子树高度 - 左子树高度 ,一旦一个节点的平衡因子大于1或者小于-1,则说明以该节点为root结点的子树不平衡,也就是上述所说的需要调整的X节点。
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<K,V>& kv):_kv(kv),_left(nullptr),_right(nullptr),_parent(nullptr),_bf(0) {}
};
2.根据平衡因子判断如何旋转
a).左左——右单旋
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR) subLR->_parent = parent;
Node* ppnode = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
if (ppnode == nullptr) { _root = subL; _root->_parent = nullptr; }
else
{
if (ppnode->_left == parent) ppnode->_left = subL;
else ppnode->_right = subL;
subL->_parent = ppnode;
}
subL->_bf = parent->_bf = 0;
}
b).右右——左单旋
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
//判断subRL是否为空,不为空才能链接其父节点
if(subRL) subRL->_parent = parent;
//记录parent的上一个结点,后续需要调整平衡因子
Node* ppnode = parent->_parent;
subR->_left = parent;
parent->_parent = subR;
//parent可能是一个树的根节点,也可能是一颗子树
if (ppnode == nullptr) { _root = subR; _root->_parent = nullptr; }
else
{
if (ppnode->_right == parent) ppnode->_right = subR;
else ppnode->_left = subR;
subR->_parent = ppnode;
}
subR->_bf = parent->_bf = 0;
}
c).左右——先左单旋后右单旋
对于双旋,涉及到的节点多,所以平衡因子的调整更为复杂
void RotateLR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
int bf = subLR->_bf;
RotateL(parent->_left);
RotateR(parent);
if (bf == -1)//subLR的左树新增结点
{
subLR->_bf = 0;
subL->_bf = 0;
parent->_bf = 1;
}
else if (bf == 1)//subLR的右树新增结点
{
parent->_bf = 0;
subL->_bf = -1;
subLR->_bf = 0;
}
else if (bf == 0)//subLR新增
{
subL->_bf = 0;
subLR->_bf = 0;
parent->_bf = 0;
}
else assert(false);
}
d).右左——先右单旋再左单旋
void RotateRL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
int bf = subRL->_bf;
//RotateL(subR);
//RotateR(subRL);
RotateR(parent->_right);
RotateL(parent);
if (bf == 1)//subRL的右节点新增
{
subR->_bf = 0;
subRL->_bf = 0;
parent->_bf = -1;
}
else if (bf == -1)
{
subR->_bf = 1;
subRL->_bf = 0;
parent->_bf = 0;
}
else if (bf == 0)
{
subR->_bf = 0;
subRL->_bf = 0;
parent->_bf = 0;
}
else assert(false);
}
3.代码实现
AVL Tree 的实现如下,我们还写了一段测试一棵树的代码是否是AVL Tree 的代码:
#pragma once
#include<assert.h>
#include<iostream>
#include<vector>
#include<cmath>
#include<utility>
using namespace std;
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<K,V>& kv):_kv(kv),_left(nullptr),_right(nullptr),_parent(nullptr),_bf(0) {}
};
template<class K,class V>
struct AVLTree
{
typedef AVLTreeNode<K, V> Node;
private:
Node* _root = nullptr;
public:
bool Insert(const pair<K, V>& kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
return true;
}
Node* parent = nullptr;
Node* cur = _root;
//找到插入位置
while (cur)
{
if (cur->_kv.first < kv.first){ parent = cur; cur = cur->_right;}
else if (cur->_kv.first > kv.first){ parent = cur; cur = cur->_left;}
else return false;
}
//进行插入链接
cur = new Node(kv);
if (parent->_kv.first < kv.first){ parent->_right = cur; cur->_parent = parent;}
else{ parent->_left = cur; cur->_parent = parent;}
//更新平衡因子
while (parent)
{
if (parent->_left == cur) parent->_bf--;
else parent->_bf++;
//调平衡
//1.parent->_bf == 0 ,即插入之前_bf == 1 或者 -1 ,不需要继续更新
//2.parent->_bf == 1 或者 -1,即插入之前_bf == 0 ,高度变换了,需要继续往上更新
//3.parnet->_bf == 2 或者 -1,即插入之前_bf == 1 或者 -1 ,需要调整
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)
{
//旋转
//1.左单旋
//2.右单旋
//3.左右双旋
//4.右左双旋
if (parent->_bf == 2 && cur->_bf == 1) RotateL(parent);
else if (parent->_bf == -2 && cur->_bf == -1) RotateR(parent);
else if (parent->_bf == 2 && cur->_bf == -1) RotateRL(parent);
else if (parent->_bf == -2 && cur->_bf == 1) RotateLR(parent);
else assert(false);
break;
}
else assert(false);
}
return true;
}
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
//判断subRL是否为空,不为空才能链接其父节点
if(subRL) subRL->_parent = parent;
//激励parent的上一个结点,后续需要调整平衡因子
Node* ppnode = parent->_parent;
subR->_left = parent;
//subR->_parent = parent->_parent;
parent->_parent = subR;
//parent可能是一个树的根节点,也可能是一颗子树
if (ppnode == nullptr) { _root = subR; _root->_parent = nullptr; }
else
{
//if (ppnode->_right == subR) ppnode->_right = subR;--------------my bug
if (ppnode->_right == parent) ppnode->_right = subR;
else ppnode->_left = subR;
subR->_parent = ppnode;
}
subR->_bf = parent->_bf = 0;
}
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR) subLR->_parent = parent;
Node* ppnode = parent->_parent;
subL->_right = parent;
//不论parent->_parent是否为空都可以链接
//subL->_parent = parent->_parent;
parent->_parent = subL;
if (ppnode == nullptr) { _root = subL; _root->_parent = nullptr; }
else
{
if (ppnode->_left == parent) ppnode->_left = subL;
else ppnode->_right = subL;
subL->_parent = ppnode;
}
subL->_bf = parent->_bf = 0;
}
void RotateLR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
int bf = subLR->_bf;
//RotateL(subL);
//RotateR(subLR);
RotateL(parent->_left);
RotateR(parent);
if (bf == -1)//subLR的左树新增结点
{
subLR->_bf = 0;
subL->_bf = 0;
parent->_bf = 1;
}
else if (bf == 1)//subLR的右树新增结点
{
parent->_bf = 0;
subL->_bf = -1;
subLR->_bf = 0;
}
else if (bf == 0)//subLR新增
{
subL->_bf = 0;
subLR->_bf = 0;
parent->_bf = 0;
}
else assert(false);
}
void RotateRL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
int bf = subRL->_bf;
//RotateL(subR);
//RotateR(subRL);
RotateR(parent->_right);
RotateL(parent);
if (bf == 1)//subRL的右节点新增
{
subR->_bf = 0;
subRL->_bf = 0;
parent->_bf = -1;
}
else if (bf == -1)
{
subR->_bf = 1;
subRL->_bf = 0;
parent->_bf = 0;
}
else if (bf == 0)
{
subR->_bf = 0;
subRL->_bf = 0;
parent->_bf = 0;
}
else assert(false);
}
void Inorder() { _Inorder(_root); }
bool isBalance() { return _isBalance(_root); }
int Height(Node* root)
{
if (root == nullptr) return 0;
int leftH = Height(root->_left);
int rightH = Height(root->_right);
return leftH > rightH ? leftH + 1 : rightH + 1;
}
private:
void _Inorder(Node* root)
{
if (root == nullptr) return;
_Inorder(root->_left);
cout << root->_kv.first << " : " << root->_kv.second << endl;
_Inorder(root->_right);
}
bool _isBalance(Node* root)
{
if (root == nullptr) return true;
int left = Height(root->_left);
int right = Height(root->_right);
if (right - left != root->_bf) { cout << "_bf error!\n"; return false; }
return abs(right - left) < 2 && _isBalance(root->_left) && _isBalance(root->_right);
}
};
void TestAVLTree()
{
srand(time(0));
const size_t N = 10000;
AVLTree<int, int> t;
for (size_t i = 0; i < N; ++i)
{
size_t x = rand();
t.Insert(make_pair(x, x));
//cout << t.IsBalance() << endl;
}
t.Inorder();
//vector<int> arr = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
vector<int> arr = { 16, 3, 7, 11, 9, 26 };
//AVLTree<int, int> t;
//for (int i = 0; i < arr.size(); i++)
//{
// t.Insert(make_pair(arr[i], arr[i]));
//}
//t.Inorder();
cout << t.isBalance() << endl;
}