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. ✨Shell脚本实现Base64 加密解密

    加密算法 # !/bin/bash # 全局变量 str="" base64_encode_string(){ # 源数据 source_string=$1 echo " ...

  2. Android集成Zxing

    1.在build文件中添加依赖 dependencies { //ZXing implementation 'com.google.zxing:core:3.3.3' implementation(' ...

  3. BuuCTF Web Writeup

    WarmUp index.php <html lang="en"> <head> <meta charset="UTF-8"> ...

  4. 最长不下降代码dp

    我看以前写过一个最长不下降,但是感觉可能没有那么好理解emmmm 下面这个是从正序寻找的emmmm 先来一个WA代码,我给写了WA的具体行数,看看其他行其实可以看出它的思路 第二个代码是AC的 #in ...

  5. 【JSOI2007】文本生成器 题解(AC自动机+动态规划)

    题目链接 题目大意:给定$n$个子串,要求构造一个长度为$m$的母串使得至少有一个子串是其子串.问方案数. ------------------------ 我们可以对要求进行转化:求出不合法的方案数 ...

  6. 2017面向对象程序设计(Java)第十七周助教工作总结

            本学期已接近尾声,java课程也即将结束.经过一学期的java学习,相信大家已经从最初的懵懂.困惑渐渐的走向了柳暗花明,并对java的体系结构有了更加清晰的认识.但一学期的学习是远远不 ...

  7. Dropzone.js文件拖拽上传提示Dropzone already attached 解决

    最近收到客户的反馈,在操作上传文件有时会出现没有任何.大部分时间是正常. 重现问题后,f12打开后台控制台发现如下提示: Uncaught Error: Dropzone already attach ...

  8. 物联网实验Arduino(1)

    回顾 我们使用的平台: Arduino 入门实验1 眨眼睛 /* Blink Turns an LED on for one second, then off for one second, repe ...

  9. Git本地仓库基本操作

    目录 设置姓名和邮箱 创建仓库 提交本地代码 .gitignore git add git commit git status git diff 查看提交记录 撤销未提交的修改 版本回退 设置姓名和邮 ...

  10. 017_go语言中的指针

    代码演示 package main import "fmt" func zeroval(ival int) { ival = 0 } func zeroptr(iptr *int) ...