提到这个就是我的痛啊,不说了...学!
目录
首先复习一下平衡二叉树AVL,这就不得不先复习二叉查找树BST
二叉查找树
递归定义:
① 要么为空树;
② 要么又根节点、左右子树构成,其中左右子树都为二叉查找树,其中右子树的数据域均大于根节点的数据域,左子树的数据域均小于根节点的数据域。
基本操作
查找:
思路:
① 若当前节点root为空,则说明查找失败,不存在该数的节点;
② 若当前节点root->val等于查询的值,则说明查找成功;
③ 若当前节点root->val大于查询的值,root=root->left,继续递归;
④ 若当前节点root->val小于查询的值,root=root->right,继续递归。
void search(node* root, int x){
if(root==NULL){
cout<<"search failed."<<endl;
return;
}
if(x==root->val){
cout<<"search succeed."<<endl;
}
else if(x>root->val){
search(root->right,x);
}
else{
search(root->left,x);
}
}
插入:
思路:
给定根节点root和需要插入的值,若root==NULL,则说明为空树,新建节点作为根节点;否则,则按照查找的步骤进行,若查找成功,则说明不需要再进行插入,该值存在于树中;若查找失败,则在该root处进行插入新节点。
void insert(node* &root, int x){
if(root==NULL){
root = newNode(x); //此处newNode为自建的新建节点API
return;
}
if(x==root->val){
cout<<"该值已经存在."<<endl;
return;
}
else if{x>root->val}{
insert(root->left,x);
}
else{
insert(root->right,x);
}
}
建树:
本质就是重复调用insert函数进行插入
node* create(int* data,int n){
node* root = NULL;
for(int i=0;i<n;i++){
insert(root,data[i]);
}
return root;
}
删除:
思路:删除的思路是四个中最复杂的,但是也是通过递归实现
① 若root为空,则说明不存在需要删除的值的节点,返回;
② 若root等于需要删除的值,则说明找到了需要删除的树节点,需要分类继续讨论:
(1)不存在子节点,为叶子节点时,也是最方便的情况,直接将该节点进行删除节点,即root=NULL;
(2)存在左子树,则将左子树中最大值向前覆盖,具体方法为找到左子树中的最大值所在节点,将其值赋给root,并删除该节点;
(3)存在右子树,则将右子树中最小值向后覆盖,具体方法为找到右子树中的最小值所在节点,将其值赋给root,并删除该节点;
(4)若左右子树都存在,只需执行(2)或(3)即可。
③ 若root大于需要删除的值,则在root的右子树中查找需要删除的树节点;
④ 若root小于需要删除的值,则在root的左子树中查找需要删除的树节点。
void delete(node* &root, int x){
if(root == NULL){
cout<<"不存在值为x的节点"<<endl;
return;
}
if(root->val == x){
if(!root->left && !root->right){
root = NULL;
}
if(root->left){
node* del = findmax(root->left); //findmax就是查询树中最大值节点,自行实现吧!
root->val = del->val;
delete(root->left, del->val);
}
if(root->right){
node* del = findmin(root->left); //findmin就是查询树中最小值节点,自行实现吧!
root->val = del->val;
delete(root->right, del->val);
}
}
else if(root->val > x){
delete(root->left, x);
}
else{
delete(root->right,x);
}
}
平衡二叉树AVL
对二叉查找树的结构优化,若插入的元素一直比前序元素大时,则会得到链式树,无法起到二叉查找树简化查找的功能。为此,引入“平衡”的概念:左右子树的高度差绝对值不超过1,左右子树的高度差称为平衡因子(左子树高度-右子树高度).
为此树结点结构需要发生一点变化:
struct node{
int v, height; //v为结点的权重,height为当前子树的高度
node *left, *right;
};
基本操作
查找(与BST完全相同)
void search(node* root, int x){
if(root==NULL){
cout<<"search failed."<<endl;
return;
}
if(x==root->val){
cout<<"search succeed."<<endl;
}
else if(x>root->val){
search(root->right,x);
}
else{
search(root->left,x);
}
}
插入
讲到插入,得先了解左右旋(不是左旋鸡腿!)
左旋步骤:
① 将B的左子树变为A的右子树;
② 让A变为B的左子树;
③ 将B设为根节点。
void Left_Rotation(node* &root){
node*temp = root->right; //root=A,temp=B
root->right = temp->left;
temp->left = root;
UpdateHeight(root);
UpdateHeight(temp);
root = temp;
}
右旋类似步骤:(代码略)
① 将A的右子树变为B的左子树;
② 让B变为A的右子树;
③ 将A设为根节点。
接下来,考虑插入结点操作,当插入一个结点时,一定会有结点的平衡因子发生变化,若有结点的平衡因子绝对值大于1,则需要进行调整。只需要把靠近插入结点的失衡结点调整,该路径上左右的结点都会平衡。
插入情况分类:可以分为LL,LR,RR,RL四种情况,本文只介绍LL型和LR型(懒...)
LL型
进行一次右旋即可实现平衡,将B变为根节点即可,在此不具体展示结果图,可参考右旋图思考。
LR型
如其名,先进行一次左旋,将其转换为LL型,再进行一次右旋,即可实现平衡, 实现图如下,最终结果图就是一个三层的满完全二叉树(不确定是不是这样叫的...)
代码实现:
void insert(node* &root, int x){
if(root==NULL){
root = newNode(x); //此处newNode为自建的新建节点API
return;
}
if(x==root->val){
cout<<"该值已经存在."<<endl;
return;
}
else if{x>root->val}{
insert(root->left,x);
UpdateHeight(root);
if(getBalanceFactor(root)==-2){
if(getBalanceFactor(root->right)==-1){ //RR型
L(root);
}
else if(getBalanceFactor(root->right)==1){ //RL型
R(root->right);
L(root);
}
}
}
else{
insert(root->right,x);
if(getBalanceFactor(root)==2){
if(getBalanceFactor(root->right)==1){ //LL型
R(root);
}
else if(getBalanceFactor(root->right)==-1){ //LR型
L(root->left);
R(root);
}
}
}
}