🚀概念
AVL树是就是平衡搜索二叉树,是对二叉树的优化,在二叉树的基础上进行了高度的调整,使其左右子树的高度差不超过1,这里的平衡就是平衡二叉树左右子树的高度。
左边的是搜索二叉树,右边的是平衡搜索二叉树,搜索二叉树的缺点在图中显而易见,这样的结构会导致它的搜索时间退化成O(n),所以AVL树就出现了
🛸平衡因子
为了实现平衡,AVL树引入了平衡因子来控制左右子树高度的平衡。那么何为平衡因子?
平衡因子 = 左右子树的高度差。
平衡因子规定不能超过2,已达到高度的平衡,所以平衡因子的取值为 0,1,-1。
🚀实现原理
因为AVL树是对搜索二叉树的改良,所以它的插入和删除就是在搜索二叉树插入和删除的基础上进行了高度调整,即子树的旋转
🛸旋转
AVL树的调整分为四种情况的旋转,即RR左单旋,LL右单旋,LR左单旋右单旋,RL右单旋左单旋
🚨LL(右单旋)
对不平衡的节点(图中为6)进行右旋
旋转规则:
将不平衡节点(6)的左子树(3)的右子树(b)链接到不平衡的节点(6)上,然后把不平衡节点(6)右旋下来作为左子树(3)的右子树,再将左子树(3)作为新的头结点。
动图作为补充:
补充几点:
- 为什么要拿这种情况的树来示例?
答:其实这种情况不是特例,而是通用的临界情况,因为子树a,b,c的高度可以任意,但必须等高。大家可以自行画图验证。- 为什么画两个新插入的节点?
答:便于理解,众所周知一次只能插入一个节点,但是插入这两个位置新结点的处理情况相同,所以一并处理。- 为什么不在b,c子树上插入新结点?
答:这是因为,在b上插入就是其他的情况了(是下面的左右单旋的情况),而在c树上插入不影响AVL树的高度平衡。
🚨RR(左单旋)
和右单选类似,图例也只是对称过来。
对不平衡的节点(图中为3)进行左旋
旋转规则:
将不平衡节点(3)的右子树(6)的左子树(b)链接到不平衡的节点(3)上,然后把不平衡节点(3)左旋下来作为右子树(6)的右子树,再将右子树(6)作为新的头结点。
动图作为补充:
为了便于学习,左右单旋的例子,我们参照左单旋和右单旋的图,只改动了b子树,将其变成了一个节点的子树,所以a,c子树可以姑且将其看作单个节点。
🚨LR(左单旋右单旋)
先对不平衡节点(6)的左节点(3)左旋转,再右旋转不平衡节点(6)。
动图作为补充:
🚨RL(右单旋左单旋)
旋转规则:
先对不平衡节点(3)的右节点(6)右旋转,再左旋转不平衡节点(3)。
动图作为补充:
值得注意的是,无论是左右单旋,还是右左单旋,它实现的难度不是在于旋转,而是在于平衡因子的设置。
🚀代码实现
既然原理我们已经清楚了,下面我们就用代码将其实现出来
- AVL树的定义
// AVL树节点
template<class K, class V>
struct Node
{
int bf; // 平衡因子
pair<K, V> val; // 值
Node<K, V>* _left; // 左节点
Node<K, V>* _right; // 右节点
Node<k, V>* _parent; // 父节点
Node(const pair<K,V>& v)
:bf(0),val(v),_left(nullptr),_right(nullptr),_parent(nullptr)
{}
};
// AVL树
typedef Node<K, V> Node;
template<class K, class V>
class AVLTree
{
public:
bool Insert(const pair<K,V>& kv)
{
// 1. 先插入
Node<K, V>* newnode = new Node<K, V>(kv);
if (_root == nullptr)
{
_root = newnode;
return true;
}
else
{
Node<K, V>* parent = nullptr;
Node<K,V>* cur = _root;
while (cur)
{
if (cur->val.first > kv.first)
{
parent = cur;
cur = cur->_left;
}
else if (cur->val.first < kv.first)
{
parent = cur;
cur = cur->_right;
}
else // 如果相等就退出,插入失败
{
return false;
}
}
// while循环走完就是找到了合适位置,但不知道是左还是右
// 判断左右位置
if (parent->val.first > newnode->val.first)
{
parent->_left = newnode;
}
else if (parent->val.first < newnode->val.first)
{
parent->_right = newnode;
}
newnode->_parent = parent;
cur = newnode;
//2. 向上调整平衡因子
while (parent)
{
if (cur == parent->_left)
parent->bf--;
else if (cur == parent->_right)
parent->bf++;
// 父节点的平衡因子修改后只可能为1/-1,2/-2或者0,
if (parent->bf == 0) // 不变
{
break;
}
else if (parent->bf == 1 || parent->bf == -1) // 影响了父节点,就可能会影响到祖宗节点,所以往上更新
{
parent = parent->_parent;
cur = cur->_parent;
}
else if (parent->bf == 2 || parent->bf == -2) // 此时就要开始旋转了
{
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);
void RotateR(Node* parent);
void RotateLR(Node* parent);
void RotateRL(Node* parent);
int _Height(Node<K, V>* root);
bool _IsBalanceTree(Node<K, V>* root);
bool IsBalanceTree();
void Inorder();
void _Inorder(Node<K,V>* root);
private:
Node* _root;
};
- 左单旋
template<class K, class V>
void AVLTree<K,V>::RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
Node* phead = parent->_parent;
parent->_right = subRL;
parent->parent = subR;
if(subRL)
subRL->_parent = parent;
subR->_left = parent;
if(phead == nullptr)
{
_root = subR;
subR->_parent = nullptr;
}
else
{
if(phead->_left == parent)
phead->_left = subR;
else if(phead->_right == parent)
phead->right = subR;
else
assert(false);
subR->_parent = phead;
}
parent->bf = subR->bf = 0;
}
- 右单旋
template<class K, class V>
void AVLTree<K,V>::RotateR(Node* parent)
{
Node* ppnode = parent->_parent;
Node* subL = parent->_left;
Node* subLR = subL->_right;
// 更新parent的连接和parent,和subLR的parent
parent->_left = subLR;
parent->_parent = subL;
if (subLR)
subLR->_parent = parent;
// 更新subL和ppnode的链接,和subL的parent
subL->_right = parent;
if (ppnode == nullptr)
{
_root = subL;
subL->_parent = nullptr;
}
else
{
if (ppnode->_left == parent)
ppnode->_left = subL;
else if (ppnode->_right == parent)
ppnode->_right = subL;
subL->_parent = ppnode;
}
parent->bf = subL->bf = 0;
}
- 左右单旋
template<class K, class V>
void AVLTree<K,V>::RotateLR(Node* parent)
{
// 记录标记,用于判断后续更新旋转完用哪种方式更新bf
Node* subL = parent->_left;
Node* subLR = subL->_right;
int bf = parent->_left->_right->bf;
RotateL(parent->_left);
RotateR(parent);
subLR->bf = 0;
if (bf == 1)
{
subL->bf = -1;
parent->bf = 0;
}
else if (bf == -1)
{
subL->bf = 0;
parent->bf = 1;
}
else if (bf == 0)
{
subL->bf = parent->bf = 0; // ?
}
else
assert(false);
}
- 右左单旋
template<class K, class V>
void AVLTree<K,V>::RotateRL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
int bf = subRL->bf;
RotateR(parent->_right);
RotateL(parent);
subRL->bf = 0;
if (bf == 1)
{
subR->bf = 0;
parent->bf = -1;
}
else if (bf == -1)
{
subR->bf = 1;
parent->bf = 0;
}
else if (bf == 0)
{
subR->bf = parent->bf = 0;
}
else
{
assert(false);
}
}
🚀测试代码
template<class K, class V>
int _Height(Node<K, V>* root)
{
if (root == nullptr)
return 0;
int left = _Height(root->_left);
int right = _Height(root->_right);
return left > right ? left + 1 : right + 1;
}
template<class K, class V>
bool _IsBalanceTree(Node<K, V>* root)
{
if (nullptr == root)
return true;
int height_left = _Height(root->_left);
int height_right = _Height(root->_right);
if (height_right - height_left != root->bf)
{
printf("两树高度差%d,%d的平衡因子为%d,不平衡\n", height_right - height_left, root->val, root->bf);
return false;
}
return abs(height_left - height_right) < 2
&& _IsBalanceTree(root->_left)
&& _IsBalanceTree(root->_right);
}
bool IsBalanceTree()
{
return _IsBalanceTree(_root);
}
void Inorder()
{
_Inorder(_root);
cout << endl;
}
template<class K, class V>
void _Inorder(Node<K,V>* root)
{
if (root == nullptr)
return;
_Inorder(root->_left);
cout << root->val.first << " ";
_Inorder(root->_right);
}
// 正确性测试
void Test()
{
vector<int> v1 = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
vector<int> v2 = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
AVLTree<int, int> t1;
AVLTree<int, int> t2;
for (int i = 0; i < v1.size(); i++)
{
t1.Insert(pair<int, int>(v1[i], 0));
t2.Insert(pair<int, int>(v2[i], 0));
}
t2.Insert(pair<int, int>(14, 0));
t1.Inorder();
cout << t1.IsBalanceTree() << endl;
t2.Inorder();
cout << t2.IsBalanceTree() << endl;
}
// 性能测试
void Test_efficiency()
{
srand(time(0));
const size_t N = 100000;
AVLTree<int, int> t;
clock_t t1 = clock();
for (int i = 0; i < N; i++)
{
int x = rand();
t.Insert(make_pair(x, x));
}
clock_t t2 = clock();
cout << "耗时:" << t2 - t1 << "ms" << endl;
cout << t.IsBalanceTree() << endl;
}
总结
AVL树的难点就在于它的旋转实现,它是在搜索二叉树的基础上优化改良了插入和删除,对整颗树的高度做了一定的限制。