关联式容器的底层结构
相信大家在编写程序的时候,会经常用到树形结构的关联式容器,然而你熟悉的这些关联式容器比如:map/set/multimap/multiset,它们的底层实现都是二叉搜素树。
二叉搜索树的性能分析
①插入和删除之前都必须先进行查找插入删除位置,所以说查找的效率代表了二叉搜索树的整体效率
②对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的深度的函数,即结点越深,则比较次数越多。
③最优情况下,二叉搜索树为完全二叉树(图一),其平均比较次数为:O(logN)
最差情况下,二叉搜索树退化为单支树(图二),其平均比较次数为:O(N/2)
综上,二叉搜索树虽然可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。如果能保证每个节点的左右子树高度之差的绝对值不超过1,即可降低树的高度,从而减少平均搜索长度。所以便有了性能改进的2.0版本AVL树。
AVL树概念
①它的左右子树都是AVL树
②左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)
AVL树的性质使它在插入和删除在极大程度上趋近于完全二叉树,由此可以避免二叉搜索树由于插入次序不当而出现单只的最差情况。而它究竟是如何调整平衡因子的呢?
AVL树的平衡因子
为了保证,我们的树是平衡的,AVL树引入了平衡因子的概念。每个节点都拥有自己的平衡因子,平衡因子的计算方式是右子树的高度减去左子树的高度,且平衡因子只有可能是-1,0,1三个数字。
AVL树的节点定义
struct AVLTreeNode
{
AVLTreeNode<K, V>* _left;
AVLTreeNode<K, V>* _right;
AVLTreeNode<K, V>* _parent;
int _bf;//平衡因子
pair<K, V> _kv;
AVLTreeNode(const pair<K, V>& kv)//进行初始化
:_left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _bf(0)
, _kv(kv)
{}
};
AVL树节点的插入
当插入的节点使上层节点发生改变,使祖先的平衡因子变为2或-2的情况这时就需要对影响祖先平衡因子改变这条路径进行旋转调整。
旋转规则
①cur节点的左树成为parent节点的右树
②parent节点成为cur节点的左树
一共有四种旋转情况,以下一一列举
一.左单旋
有了上面的基础我们再增加一些节点:
看懂以上,我们总结一下它的抽象图:
下面我们来看看左单旋是如何实现的
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL) //subRL要判断存在,否则不能对空解引用
subRL->_parent = parent;
subR->_left = parent;
Node* parentParent = parent->_parent; //要记录父节点在祖父节点的左边还是右边,以便判断cur在祖父节点的左边还是右边
parent->_parent = subR;
if (_root == parent)
{
_root = subR;
subR->_parent = NULL;
}
else
{
if (parentParent->_left == parent)
{
parentParent->_left = subR;
}
else
{
parentParent->_right = subR;
}
subR->_parent = parentParent;
}
parent->_bf = subR->_bf = 0;
}
二.右单旋
同理,以下为右单旋的过程:
右单旋抽象图:
三.右左双旋
我们再来看个特别的情况:
细心的同学可能发现了一个规律:这三种情况都出自右左双旋这个旋转规律,但是旋转之后的平衡因子似乎不大相同;
void RotateRL(Node* parent)
{
Node* SubR = parent->_right;
Node* SubRL = SubR->_left;
int bf = SubRL->_bf;//记录右左双旋之前SubRL的平衡因子
RotateR(parent->_right);
RotateL(parent);
if (bf == 1)//如果之前为1,对照上图插入在SubRL右侧的情况
{
SubR->_bf = 0;
parent->_bf = -1;
}
else if (bf == -1)//如果之前为-1,对照上图插入在SubRL左侧的情况
{
SubR->_bf = 1;
parent->_bf = 0;
}
else if (bf == 0)//说明我自己就是刚刚插入的节点
{
parent->_bf = SubR->_bf = 0;
}
SubRL->_bf = 0;//SubRL最后的平衡因子总为0
}
四.左右双旋
左右双旋与右左双旋类似,所以这里简单附上一个具象图
void RotateLR(Node* parent)//对照抽象图动手画一画会变得非常明了
{
Node* SubL = parent->_left;
Node* SubLR = SubL->_right;
int bf = SubLR->_bf;
RotateL(parent->_left);
RotateR(parent);
if (bf == 1)
{
SubL->_bf = -1;
parent->_bf = 0;
}
else if (bf == -1)
{
SubL->_bf = 0;
parent->_bf = 1;
}
else if (bf == 0)
{
parent->_bf = SubL->_bf = 0;
}
SubLR->_bf = 0;
}
总结:
假如以pParent为根的子树不平衡,即pParent的平衡因子为2或者-2,分以下情况考虑
1、pParent的平衡因子为2,说明pParent的右子树高,设pParent的右子树的根为pSubR
a、当pSubR的平衡因子为1时,执行左单旋
b、当pSubR的平衡因子为-1时,执行右左双旋
2、pParent的平衡因子为-2,说明pParent的左子树高,设pParent的左子树的根为pSubL
a、当pSubL的平衡因子为-1是,执行右单旋
b、当pSubL的平衡因子为1时,执行左右双旋
旋转完成后,原pParent为根的子树个高度降低,已经平衡,不需要再向上更新
AVL树的验证
AVL树是在二叉搜索树的基础上加入了平衡性的限制,因此要验证AVL树,可以分两步:
1、 验证其为二叉搜索树
如果中序遍历可得到一个有序的序列,就说明为二叉搜索树
2、 验证其为平衡树
每个节点子树高度差的绝对值不超过1(注意节点中如果没有平衡因子) 节点的平衡因子是否计算正确
int _Height(Node* pRoot)
{
if (nullptr == pRoot)
return 0;
int leftHeight = _Height(pRoot->_pLeft);
int rightHeight = _Height(pRoot->_pRight);
return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}
bool _IsAVLTree(Node* pRoot)
{
// 空树也是AVL树
if (nullptr == pRoot)
return true;
// 计算pRoot节点的平衡因子:即pRoot左右子树的高度差
int leftHeight = _Height(pRoot->_pLeft);
int rightHeight = _Height(pRoot->_pRight);
// 如果计算出的平衡因子与pRoot的平衡因子不相等,或者
// pRoot平衡因子的绝对值超过1,则一定不是AVL树
if (abs(rightHeight - leftHeight) > 1 || rightHeight - leftHeight != pRoot->_bf)
return false;
// pRoot的左和右如果都是AVL树,则该树一定是AVL树
return _IsAVLTree(pRoot->_pLeft) && _IsAVLTree(pRoot->_pRight);
}
AVL树性能分析
AVL树平衡的特性可以保证查询时高效的时间复杂度,但是如果要对AVL树做一些结构修改的操作,性能非常低下,比如: 插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。 因此:如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树, 但一个结构经常修改,就不太适合。
以下为AVL树完整版代码
#include<iostream>
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)
:_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_bf(0)
,_kv(kv)
{
}
};
template<class K,class V>
class AVLTree
{
typedef AVLTreeNode<K, V> Node;
public:
V& operator[](const K& key)
{
pair<Node*, bool> ret = Insert(make_pair(key, V()));
return ret.first->_kv.second;
}
pair<Node*, bool>Insert(const pair<K, V>& kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
return make_pair(_root, true);
}
//1.按照搜索树的规则进行插入
Node* parents = 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 make_pair(cur, false);
}
}
Node* newnode = new Node(kv);
cur = newnode;
cur->_bf = 0;
if (parent->_kv.first < kv.first)
{
parent->_right = cur;
cur->_parent = parent;
}
else {
parent->_left = cur;
cur->_parent = parent;
}
//平衡问题
//2.更新平衡因子
while (parent)
{
if (cur == parent->_right)
parent->_bf += 1;
else
parent->_bf -= 1;
if (parent->_bf == 1 || parent->_bf == -1)
{
cur = cur->_parent;
parent = parent->_parent;
}
else if (parent->_bf == 0)
{
break;
}
else if (parent->_bf == 2 || parent->_bf == -2)
{
//旋转处理
if (parent->_bf == -2 && cur->_bf == -1)
{
RotateR(parent);
}
else if (parent->_bf == 2 && cur->_bf == 1)
{
RotateL(parent);
}
else if (parent->_bf == -2 && cur->_bf == 1)
{
RotateRL(parent);
}
else
{
assert(false);
}
break;
}
}
return make_pair(newnode, true);
}
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
subR->_left = parent;
Node* parentParent = parent->_parent;
parent->_parent = subR;
if (_root == parent)
{
_root = subR;
subR->_parent = NULL;
}
else
{
if (parentParent->_left == parent)
{
parentParent->_left = subR;
}
else
{
parentParent->_right = subR;
}
subR->_parent = parentParent;
}
parent->_bf = subR->_bf = 0;
}
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
subL->_right = parent;
Node* parentParent = parent->_parent;
parent_parent = subL;
if (parent == _root)
{
_root = subL;
subL->_parent = nullptr;
}
else
{
if (parentParent->_left == parent)
{
parentParent->_left = subL;
}
else
{
parentParent->_right = subL;
}
subL->_parent = parentParent;
}
subL->_bf = parent->_bf = 0;
}
void RotateLR(Node* parent)//对照抽象图动手画一画会变得非常明了
{
Node* SubL = parent->_left;
Node* SubLR = SubL->_right;
int bf = SubLR->_bf;
RotateL(parent->_left);
RotateR(parent);
if (bf == 1)
{
SubL->_bf = -1;
parent->_bf = 0;
}
else if (bf == -1)
{
SubL->_bf = 0;
parent->_bf = 1;
}
else if (bf == 0)
{
parent->_bf = SubL->_bf = 0;
}
SubLR->_bf = 0;
}
void RotateRL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
int bf = subRL->_bf;
RotateR(parent->_right);
RotateL(parent);
if (bf == 1)
{
subRL->_bf = 0;
parent->_bf = -1;
subR->_bf = 0;
}
else if (bf == -1)
{
subRL->_bf = 0;
parent->_bf = 0;
subR->_bf = 1;
}
else if (bf == 0)
{
subRL->_bf = 0;
parent->_bf = 0;
subR->_bf = 0;
}
}
int Height(Node* root)
{
if (root == nullptr)
return 0;
int leftHeight = Height(root->_left);
int rightHeight = Height(root->_right);
return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}
bool _IsBalance(Node* root)
{
if (root == nullptr)
return true;
int leftHeight = Height(root->_left);
int rightHeight = Height(root->_right);
if (rightHeight - leftHeight != root->_bf)
{
cout << root->_kv.first << ":平衡因子异常" << endl;
return false;
}
return abs(leftHeight - rightHeight) < 2
&& _IsBalance(root->_left)
&& _IsBalance(root->_right);
}
bool IsBalance()
{
return _IsBalance(_root);
}
private:
Node* _root = nullptr;
};
void TestAVLTree()
{
//AVLTree<int, double> t;
//t.Insert(make_pair(1, 1.1));
//t.Insert(make_pair(2, 2.2));
t[3];
//t[3] = 3.3;
//t[4] = 4.4;
//t[5] = 5.5;
//t[6] = 6.6;
int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
AVLTree<int, int> t;
for (auto e : a)
{
t.Insert(make_pair(e, e));
}
cout << t.IsBalance() << endl;
}