AVL树高度平衡的二叉搜索树,任一点的平衡印章只能是+1、-1、0,从而尽量降低树的高度。

如果它有n个结点,高度可保持在O(log2n),平均搜索长度也可保持在O(log2n)。

(1)AVL树的插入

在插入一个新结点时,需要从插入位置沿通向根的路径回溯,检查各结点左右子树高度差。

发现结点的平衡因子为0,刚刚是在其较矮的子树插入新结点,从该结点到根的路径上各结点为根的子树高度不变、平衡因子不变,无需继续检查。

发现结点平衡因子|bf|=1,说明插入前是0,插入后该结点没有失去平衡。但该子树高度增加了,还需要继续往根方向检查。

发现某一结点|bf|=2不平衡,停止回溯,做平衡化旋转。

从发生不平衡的结点起,沿刚才回溯的路径取直接下两层的结点。

1.这三个结点位于同一条直线上,采用单旋转进行平衡化

bf=2,右子树高,查看右结点bf=1,执行左单旋转

执行后ptr和subL的bf均变为0:

bf=-2,左子树高,查看左结点bf=-1,执行右单旋转

执行后ptr和subL的bf均变为0

2.这三个结点位于一条折线上,采用双旋转进行平衡化

bf=2,右子树高,查看右结点bf=-1,无论ptr->bf是1还是-1,都执行先右后左双旋转

如果ptr的bf原为1,右单旋转后subR的bf改为0,先右后左双旋转,旋转后subL的bf变为-1

如果ptr的bf原为-1,右单旋转后subR的bf改为1,先右后左双旋转,旋转后subL的bf变为0

ptr的bf最终变为0

bf=-2,左子树高,查看左结点bf=1,无论ptr->bf是1还是-1,都执行先左后右双旋转

如果ptr的bf原为-1,左单旋转后subL的bf改为0,先左后右双旋转,旋转后subR的bf变为1

如果ptr的bf原为1,左单旋转后subL的bf改为-1,先左后右双旋转,旋转后subR的bf变为0

ptr的bf最终变为0

(2)AVL树的删除

1.被删结点有两个子女

寻找被删结点p在中序下的直接前驱或者直接后继q,把q的值给结点p,然后把结点q删除(q最多一个子女)

2.被删结点最多一个子女q

直接把p的父结点pr中原来指向p的指针指向q。

q是pr的左子女的话,pr的bf加1;q是pr的右子女的话,pr的bf减1。

(1)pr的bf原为0,左/右子树被缩短后改为1/-1,以pr为根的子树高度未变,pr到根结点的路径上所有结点都不需要调整。

(2)pr的bf原不为0,较高的子树被缩短,bf变为0。

以pr为根的子树虽然平衡,但高度减少了。需要继续考察其父结点。

(3)pr的bf原不为0,较矮的子树又被缩短,bf变为±2。令q指向pr的较高的子树

①q->bf==0的情况:

pr的bf=2,右子树较高,右子树q的bf为0,作左单旋转

旋转后q->bf=pr->bf=0,还要把q->pf改为-1,pr->bf改为1

pr的bf=-2,左子树较高,左子树q的bf为0,作右单旋转

旋转后q->bf=pr->bf=0,还要把q->pf改为1,pr->bf改为-1

②pr->bf和q->bf正负号相同的情况:

pr的bf=2,右子树较高,右子树q的bf=1,执行左单旋转,旋转后q->bf=pr->bf=0

pr的bf=-2,左子树较高,左子树q的bf=-1,执行右单旋转,旋转后q->bf=pr->bf=0

③pr和p的bf正负号相反,执行双旋转,先绕q转一次,再绕pr转一次:

pr的bf=2,右子树较高,右子树q的bf为-1,执行先右后左双旋转

pr的bf=-2,左子树较高,左子树q的bf为1,执行先左后右双旋转

处理后子树高度减少1,继续考察其父结点。

#include <iostream>
#include "stack.h"
using namespace std; template <class E,class K>
struct AVLNode:public BSTNode<E,K>{
int bf; //右子树高度-左子树高度
AVLNode():left(NULL),right(NULL),bf(){}
AVLNode(E d,AVLNode<E,K> *l=NULL,AVLNode<E,K> *r=NULL):data(d),left(l),right(r),bf(){}
} template <class E,class K>
class AVLTree:public BST<E,K>{
public:
AVLTree():root(NULL){}
AVLTree(K Ref):RefValue(Ref),root(NULL){}
bool Insert(E& el){return Insert(root,el);}
bool Remove(K x,E& el){return Remove(root,el);}
friend istream& operator>>(istream& in,AVLTree<E,K>& tree);
friend ostream& operator<<(ostream& out,AVLTree<E,K>& tree);
int Height()const;
protected:
AVLNode<E,K> *Search(K x,AVLNode<E,K>* &par)const;
bool Insert(AVLNode<E,K>* &ptr,E& el);
bool Remove(AVLNode<E,K>* &ptr,K x,E& el);
void RotateL(AVLNode<E,K>* &ptr);
void RotateR(AVLNode<E,K>* &ptr);
void RotateLR(AVLNode<E,K>* &ptr);
void RotateRL(AVLNode<E,K>* &ptr);
int Height(AVLNode<E,K> *ptr)const;
void AVLTree<E,K>::Traverse(AVLNode<E,K> *ptr,ostream& out)const;
} template <class E,class K>
void AVLTree<E,K>::RotateL(AVLNode<E,K>* &ptr){
//右子树比左子树高,对以ptr为根的AVL树做左单旋转,旋转后新根在ptr
AVLNode<E,K> *subL=ptr;
ptr=subL->right; //两个结点平衡因子均为正,需要做左单旋转
subL->right=ptr->left;
ptr->left=subL;
ptr->bf=subL->bf=;
} template <class E,class K>
void AVLTree<E,K>::RotateR(AVLNode<E,K>* &ptr){
//左子树比右子树高,对以ptr为根的AVL树做右单旋转,旋转后新根在ptr
AVLNode<E,K> *subR=ptr;
ptr=subR->left; //两个结点平衡因子均为负,需要做右单旋转
subR->left=ptr->right;
ptr->right=subR;
ptr->bf=subR->bf=;
} template <class E,class K>
void AVLTree<E,K>::RotateLR(AVLNode<E,K>* &ptr){
AVLNode<E,K> *subR=ptr,*subL=ptr->left;
ptr=subL->right;
subL->right=ptr->left;
ptr->left=subL;
if(ptr->bf<=) subL->bf=; //如果ptr->bf原为-1,左单旋转后subL->bf为0
else subL->bf=-; //如果ptr->bf原为1,左单旋转后subL->bf为-1
subR->left=ptr->right;
ptr->right=subR;
if(ptr->bf==-) subR->bf=; //如果ptr->bf原为-1,先左后双旋转后subR->bf为1
else subR->bf=; //如果ptr->bf原为1,先左后双旋转后subR->bf为0
ptr->bf=;
} template <class E,class K>
void AVLTree<E,K>::RotateRL(AVLNode<E,K>* &ptr){
AVLNode<E,K> *subL=ptr,*subR=ptr->right;
ptr=subR->left;
subR->left=ptr->right;
ptr->right=subR;
if(ptr->bf>=) subR->bf=;
else subR->bf=;
subL->right=ptr->left;
ptr->left=subL;
if(ptr->bf==) subL->bf=-;
else subL->bf=;
ptr->bf=;
} template <class E,class K>
bool AVLTree<E,K>::Insert(AVLNode<E,K>* &ptr,E& el){ //改变bf的步骤在旋转中做了,Insert方法中无需再改变
AVLNode<E,K> *pr=NULL,*p=ptr,*q;
int d;
stack<AVLNode<E,K>*> st;
while(p!=NULL){
if(el==p->data) return false;
pr=p;
st.push(pr);
if(el<p->data) p=p->left;
else p=p->right;
}
p=new AVLNode<E,K>(el);
if(p==NULL){
cerr<<"存储空间不足!"<<endl;
exit();
}
if(pr==NULL){ //空树,新结点成为根结点
ptr=p;
return true;
}
if(el<pr->data) pr->left=p;
else pr->right=p;
while(!st.IsEmpty()){
st.Pop(pr);
if(p==pr->left) pr->bf--; //调整父结点平衡因子
else pr->bf++;
//第一种情况,平衡因子为0,说明刚才在pr较矮的子树上插入新结点,结点pr处平衡且高度未变。结点pr到根的路径上所有结点为根的子树高度都未变。可以直接结束重新平衡化的处理。
if(pr->bf==) break;
//第二种情况,平衡因子绝对值为1,说明插入前平衡因子为0,插入后以pr为根的子树没有失去平衡,但仍然需要检查结点pr到根的路径上所有结点为根的子树
if(pr->bf== || pr->bf==-) p=pr;
//第三种情况,平衡因子绝对值为2,说明新结点在较高的子树上插入,造成了不平衡,需要做平衡化旋转
else{
d=(pr->bf<)?-:;
if(p->bf==d){ //两结点平衡因子同号,单旋转
if(d==-) RotateR(pr); //pr的bf为-2,p的bf为1,右单旋转
else RotateL(pr); //pr的bf为2,p的bf为-1,左单旋转
}
else{
if(d==-) RotateLR(pr); //pr的bf为2,p的bf为-1,先右后左旋转
else RotateRL(pr); //pr的bf为-2,p的bf为-1,先左后右旋转
}
break; //旋转之后以pr为根的子树高度降低,无需再向上层回溯
}
}
if(st.IsEmpty()) ptr=pr; //栈空,pr作为新根
else{
st.getTop(q); //栈不空,取出pr的父结点
if(q->data>pr->data) q->left=pr;
else q->right=pr;
}
return true;
} template <class E,class K>
istream& operator>>(istream& in,AVLTree<E,K>& Tree){
E item;
cout<<"Construct AVL tree:\n";
cout<<"Input Data(end with"<<Tree.RefValue<<"):";
in>>item;
while(item.key!=Tree.RefValue){
Tree.Insert(item);
cout<<"Input Data(end with"<<Tree.RefValue<<"):";
in>>item;
}
return in;
} template <class E,class K>
ostream& operator<<(ostream& out,const AVLTree<E,K>& Tree){
out<<"Inorder traversal of AVL tree.\n";
Tree.Traverse(Tree.root,out);
out<<endl;
return out;
} template <class E,class K>
void AVLTree<E,K>::Traverse(AVLNode<E,K> *ptr,ostream& out)const{
if(ptr!=NULL){
Traverse(ptr->left,out);
out<<ptr->data<<'';
Traverse(ptr->right,out);
}
} template <class E,class K>
bool AVLTree<E,K>::Remove(AVLNode<E,K>* &ptr,K x,E& el){
AVLNode<E,K> *pr=NULL,*p=ptr,*q,*ppr;
int d,dd=;
while(p!=NULL){
if(k==p->data.key) break;
pr=p;
st.Push(pr);
if(k<p->data.key) p=p->left;
else p=p->right;
}
if(p==NULL) return false;
if(p->left!=NULL && p->right!=NULL){ //被删结点有两个子女
pr=p;
st.Push(pr);
q=p->left; //在p的左子树找p的直接前驱
while(q->right!=NULL){
pr=q;
st.Push(pr);
q=q->right;
}
p->data=q->data;
p=q; //被删结点转换为q,q最多一个子女
}
if(p->left!=NULL) q=p->left; //被删结点只有一个子女或者没有子女
else q=p->right;
if(pr==NULL) ptr=q; //被删结点为根结点,让其子女成为新根
else{ //被删结点不是根结点
if(pr->left==p) pr->left=q; //p的父结点pr原本指向p的指针改指向q
else pr->right=q;
while(!st.IsEmpty()){ //重新平衡化
st.Pop(pr); //从栈中推出父结点
if(pr->right==q) pr->bf--; //调整父结点的平衡因子
else pr->bf++;
if(!st.IsEmpty()){
st.getTop(ppr); //从栈中取出祖父结点
dd=(ppr->left==pr)?-:; //旋转后与上层链接方向
}
else dd=; //栈空,旋转后不与上层链接
if(pr->bf== || pr->bf==-)//pr的平衡因子原为0,在它的左/右子树被缩短后,平衡因子变为1或者-1,以pr为根的子树高度没有改变,从pr到根结点的路径上所有结点都不需要调整
break;
if(pr->bf!=){
if(pr->bf<){d=-;q=pr->left;}
else{d=;q=pr->right;}
if(q->bf==){
if(d==-){ //pr的平衡因子为-2,左子树q的平衡因子为0,执行右单旋转
RotateR(pr);
pr->bf=;
pr->left->bf=-;
}
else{ //pr的平衡因子为2,右子树q的平衡因子为0,执行左单旋转
RotateL(pr);
pr->bf=-;
pr->right->bf=;
}
break;
}
if(q->bf==d){ //q和pr的平衡因子正负号相同,执行一个单旋转来恢复平衡
if(d==-) RotateR(pr); //pr平衡因子为-2,p平衡因子为-1,右单旋转
else RotateL(pr); //pr平衡因子为2,p平衡因子为1,左单旋转
}
else{ //q和pr的平衡因子正负号相反,执行一个双旋转来恢复平衡
if(d==-) RotateLR(pr); //pr的bf=-2,左子树较高,左子树p的bf为1,执行先左后右双旋转
else RotateRL(pr); //pr的bf=2,右子树较高,右子树p的bf为-1,执行先右后左双旋转
}
if(dd==-) ppr->left=pr;
else if(dd==) ppr->right=pr; //旋转后新根与上层链接
}
q=pr; //pr的平衡因子变为0(较高的子树被缩短了),此时以pr为根的树平衡但高度减少1,需要继续考察pr父结点的平衡状态
}
if(st.IsEmpty()) ptr=pr; //调整到树的根结点
}
delete p;
return true;
}

算法-搜索(3)AVL树的更多相关文章

  1. 数据结构与算法(九):AVL树详细讲解

    数据结构与算法(一):基础简介 数据结构与算法(二):基于数组的实现ArrayList源码彻底分析 数据结构与算法(三):基于链表的实现LinkedList源码彻底分析 数据结构与算法(四):基于哈希 ...

  2. [算法] 数据结构之AVL树

    1 .基本概念 AVL树的复杂程度真是比二叉搜索树高了整整一个数量级——它的原理并不难弄懂,但要把它用代码实现出来还真的有点费脑筋.下面我们来看看: 1.1  AVL树是什么? AVL树本质上还是一棵 ...

  3. 数据结构与算法——平衡二叉树(AVL树)

    目录 二叉排序树存在的问题 基本介绍 单旋转(左旋转) 树高度计算 旋转 右旋转 双旋转 完整代码 二叉排序树存在的问题 一个数列 {1,2,3,4,5,6},创建一颗二叉排序树(BST) 创建完成的 ...

  4. 红黑树与AVL树

    概述:本文从排序二叉树作为引子,讲解了红黑树,最后把红黑树和AVL树做了一个比较全面的对比. 1 排序二叉树 排序二叉树是一种特殊结构的二叉树,可以非常方便地对树中所有节点进行排序和检索. 排序二叉树 ...

  5. 平衡搜索树(一) AVL树

    AVL树 AVL树又称为高度平衡的二叉搜索树,是1962年有俄罗斯的数学家G.M.Adel'son-Vel'skii和E.M.Landis提出来的.它能保持二叉树的高度 平衡,尽量降低二叉树的高度,减 ...

  6. 深入浅出数据结构C语言版(12)——平衡二叉查找树之AVL树

    在上一篇博文中我们提到了,如果对普通二叉查找树进行随机的插入.删除,很可能导致树的严重不平衡 所以这一次,我们就来介绍一种最老的.可以实现左右子树"平衡效果"的树(或者说算法),即 ...

  7. AVL树的平衡算法(JAVA实现)

      1.概念: AVL树本质上还是一个二叉搜索树,不过比二叉搜索树多了一个平衡条件:每个节点的左右子树的高度差不大于1. 二叉树的应用是为了弥补链表的查询效率问题,但是极端情况下,二叉搜索树会无限接近 ...

  8. AVL树的插入删除查找算法实现和分析-1

    至于什么是AVL树和AVL树的一些概念问题在这里就不多说了,下面是我写的代码,里面的注释非常详细地说明了实现的思想和方法. 因为在操作时真正需要的是子树高度的差,所以这里采用-1,0,1来表示左子树和 ...

  9. 算法与数据结构(十一) 平衡二叉树(AVL树)

    今天的博客是在上一篇博客的基础上进行的延伸.上一篇博客我们主要聊了二叉排序树,详情请戳<二叉排序树的查找.插入与删除>.本篇博客我们就在二叉排序树的基础上来聊聊平衡二叉树,也叫AVL树,A ...

随机推荐

  1. Java容器学习之List

    List接口继承了Collcetion接口,Collection接口又继承了超级接口Iterable,List是有序列表,实现类有ArrayList.LinkedList.Vector.Stack等. ...

  2. python学习笔记1 -- 函数式编程之高阶函数 map 和reduce

    我用我自己,就是高阶函数,直接表现就是函数可以作为另一个函数的参数,也可以作为返回值 首先一个知识点是 函数的表现形式,印象中的是def  fw(参数)这种方式定义一个函数 python有很多的内置函 ...

  3. 删除GIT中的.DS_Store

    转载自:https://www.jianshu.com/p/fdaa8be7f6c3 .DS_Store 是什么 使用 Mac 的用户可能会注意到,系统经常会自动在每个目录生成一个隐藏的 .DS_St ...

  4. pandas_读取Excel并筛选特定数据

    # C:\Users\lenovo\Desktop\总结\Python # 读取 Excel 文件并进行筛选 import pandas as pd # 设置列对齐 pd.set_option(&qu ...

  5. FPAG_Microblaze_PWM_定时器

    由于Xilinx底层库的定时器没有PWM例程,调试过程中费了不少劲.生产PWM需要两个定时器同时工作,一个控制频率,一个控制占空比,位数可通过硬件设置. #include "xtmrctr_ ...

  6. Python os.lseek() 方法

    概述 os.lseek() 方法用于设置文件描述符 fd 当前位置为 pos, how 方式修改.高佣联盟 www.cgewang.com 在Unix,Windows中有效. 语法 lseek()方法 ...

  7. Python math 模块、cmath 模块

    Python math 模块.cmath 模块 Python 中数学运算常用的函数基本都在 math 模块.cmath 模块中.高佣联盟 www.cgewang.com Python math 模块提 ...

  8. 实验02——java两个数交换的三种解决方案

    package cn.tedu.demo;/** * @author 赵瑞鑫      E-mail:1922250303@qq.com * @version 1.0* @创建时间:2020年7月16 ...

  9. 秦九韶算法 & 三分法

    前言 今天考试出了一个题 郭郭模拟退火骗了75分 于是再次把咕咕了好久的模退提上日程 如果进展顺利 明后天应该会开爬山算法和模退的博客笔记 今天先把今天考试的正解学习一下--三分法 引入 老规矩上板子 ...

  10. Spring Cloud 之分布式配置基础应用

    分布式配置基础应用 配置中心服务 spring-config-server pom.xml <?xml version="1.0" encoding="UTF-8& ...