前言
二叉搜索树是map/set/multiset/multimap的底层实现逻辑。二叉搜索树如果在插入的元素有序的话,就会成为一个单向链表。所以增加了平衡因子来调整结构。
特点:1、AVL树的左右子树的高度之差不超过绝对值1。
2、一个二叉搜索树如果高度平衡,它就是AVL树,假设ta有n个结点,其高度O(log2n),搜索时间复杂度O(log2n)。
但是,AVL树不一定有平衡因子,使用平衡因子只是它的一种实现方式。(对于这点目前有点疑惑,如果没有平衡因子,它又怎么判断是否该调整呢?)
关于AVL树的代码笔记
二叉搜索树
template<class K,class V>
struct AVL_TreeNode
{
AVL_TreeNode<K,V>* _left;
AVL_TreeNode<K,V>* _right;
AVL_TreeNode<K,V>* _parent;
pair<K,V> _KV; //键值对
int _bf; //balance factor的缩写
AVL_TreeNode(const pair<K,V>& kv)
:_left(nullptr),
_right(nullptr),
_parent(nullptr),
_kv(kv),
_bf(0)
{};
};
template<class K,class V>
class AVL_Tree
{
typedef AVL_TreeNode<K,V> Node;
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->_left=cur;
}
else
{
parent->_right=cur;
}
cur->_parent=parent;
//搜索树部分结束
//接下来是平衡因子的更新,根据parent的_bf的变化进行更新
//向左放置-1,向右放置+1,本质是左右子树高度差
while(parent)
{
if(cur==parent->_right)
{
parent->_bf++;
}
else
{
parent->_bf--;
}
if(parent->_bf==1||parent->_bf==-1)//向前更新
{
parent=parent->_parent;
cur=cur->_parent;
}
else if(parent->_bf==0)
{
break;//没问题不更新
}
else if(parent->_bf==2||parent->_bf==-2)//树的两边不平衡了,需要处理
{
//处理的情况单独写。
}
}
return true;
}
private:
Node* _root=nullptr;
};
AVL 树的旋转
新节点插入到较高右子树的右侧---右右:左单旋
这里的A/B/C是这个树的子树,高度为h(h是可以=0的,这会是个伏笔)右子树高h+1,左子树高h,所以root结点的_bf为h+1-h=1。 同理,依次类推。
然后+了一个结点,变成这样了,平衡被打破,需要旋转。
旋转之后成了这样,这就是右右-左单旋的过程。
代码实现
void LeftRotation(Node* root)
{
Node* son = root->_right;
Node* sonleft = son->_left;
root->_right = sonleft;
if(sonleft)
{
sonleft->_parent = root;
}
Node* rootparent = parent->_parent;//这里所谓的根是相对而言的,
//实际上的平衡因子的破坏一般是由局部到整体
son->_left = root;
root->_parent = son;
if (rootparent == nullptr)
{
_root = son;
_root->_parent = nullptr;
}
else
{
if (rootparent->_left == root)
{
rootparent->_left = son;
}
else
{
rootparent->_right = son;
}
son->_parent = rootparent;
}
root->_bf = son->_bf = 0;
}
新节点插入到较高左子树的左侧---左左:右单旋
然后左边的左子树新增了一个结点
调整后就成了这样 了
代码实现:
void rightRotation(Node* root)
{
Node* son = root->_left;
Node* sonright = son->_right;
Node* rootparent = root->_parent;
root->_left = sonright;
if (sonright)
{
sonright->_parent = root;
}
son->_right = root;
root->_parent = son;
if (rootparent == nullptr)
{
_root = son;
_root->_parent = nullptr;
}
else
{
if (rootparent->_left == root)
{
rootparent->_left = son;
}
else
{
rootparent->_right = son;
}
son->_parent = rootparent;
}
root->_bf = son->_bf = 0;
}
新节点插入较高左子树的右侧---左右:先左单旋再右单旋
这里有两种情况
一种是这样的,(子树高度h=0的情况)
新增结点后成了这样
还有另一种情况就是 这样
这里要说明一下,A和D子树高度为h,B和C子树的高度为h-1.然后新增了一个结点之后成了这样了
然后进行调整。
当然还有一个在C的位置插入的,变化和上面差不多原理,只是最后son是-1,root是0,所以需要把son2的_bf值先记录下来。
代码实现 :
void LeftandRightRotation(Node* root)
{
Node* son = root->_left;
Node* son2 = son->_right;
//先保存son2的平衡因子,需要在之后根据数字来对其他的进行平衡
if(son2bf)
{
int son2bf = son2->_bf;
}
else
{
int son2bf=0;//将第一种情况单独列出来
}
LeftRotation(son);
rightRotation(root);
if (1 == son2bf)
{
son->_bf = -1;
}
else if (-1 == son2bf)
{
root->_bf = 1;
}
}
新节点插入较高右子树的左侧---右左:先右单旋再左单旋
一开始和前面的类似,这里从新增结点的时候,平衡因子变化开始。同样的,B和C是高度为h-
1的子树,A和D是高度为h的子树。
然后进行调整
这里也有另一种情况,就是son2->_bf=1的情况 ,最后的结果也是root->_bf=-1,son->_bf=0,
也需要对son2的bf进行记录
还有一个情况是这样的,不可忽略,
代码实现如下:
void RightandLeftRotation(Node* root)
{
Node* son = root->_right;
Node* son2 = son->_left;
if(son2bf)
{
int son2bf = son2->_bf;
}
else
{
int son2bf=0;//将特殊情况单独列出来
}
rightRotation(son);
LeftRotation(root);
if (1 == son2bf)
{
root->_bf = -1;
}
else if (-1 == son2bf)
{
son->_bf = 1;
}
}