AVL是一种平衡二叉树,它通过对二叉搜索树中的节点进行旋转使得二叉搜索树达到平衡。AVL在所有的平衡二叉搜索树中具有最高的平衡性。

定义

平衡二叉树或者为空树或者为满足如下性质的二叉搜索树:

  1. 左右子树的高度之差绝对值不超过1
  2. 左右子树仍然为平衡二叉树

定义平衡因子 BF(x) = x的左子树高度 - x的右子树的高度。平衡二叉树的每个节点的平衡因子只能为-1, 0, 1.

维持平衡思想

若二叉树当前为平衡状态,此时插入/删除一个新的节点,此时有可能造成二叉树不满足平衡条件,此时需要通过对节点进行旋转来使得二叉树达到平衡状态。从被插入/删除的节点开始向上查找,找到第一个不满足 |BF| <= 1的祖先节点P,此时对P和P的子节点(或P的子节点的子节点)进行旋转(可能分为下面的四种情况)。

旋转

(1)LL型平衡旋转法 
    由于在A的左孩子B的左子树上插入结点F,使A的平衡因子由1增至2而失去平衡。故需进行一次顺时针旋转操作。 即将A的左孩子B向右上旋转代替A作为根结点,A向右下旋转成为B的右子树的根结点。而原来B的右子树则变成A的左子树。 

(2)RR型平衡旋转法 
    由于在A的右孩子C 的右子树上插入结点F,使A的平衡因子由-1减至-2而失去平衡。故需进行一次逆时针旋转操作。即将A的右孩子C向左上旋转代替A作为根结点,A向左下旋转成为C的左子树的根结点。而原来C的左子树则变成A的右子树。 

(3)LR型平衡旋转法 
    由于在A的左孩子B的右子数上插入结点F,使A的平衡因子由1增至2而失去平衡。故需进行两次旋转操作(先逆时针,后顺时针)。即先将A结点的左孩子B的右子树的根结点D向左上旋转提升到B结点的位置,然后再把该D结点向右上旋转提升到A结点的位置。即先使之成为LL型,再按LL型处理。 
 
    如图中所示,即先将圆圈部分先调整为平衡树,然后将其以根结点接到A的左子树上,此时成为LL型,再按LL型处理成平衡型。

(4)RL型平衡旋转法 
    由于在A的右孩子C的左子树上插入结点F,使A的平衡因子由-1减至-2而失去平衡。故需进行两次旋转操作(先顺时针,后逆时针),即先将A结点的右孩子C的左子树的根结点D向右上旋转提升到C结点的位置,然后再把该D结点向左上旋转提升到A结点的位置。即先使之成为RR型,再按RR型处理。 
 
    如图中所示,即先将圆圈部分先调整为平衡树,然后将其以根结点接到A的左子树上,此时成为RR型,再按RR型处理成平衡型。

实现(c++)

#include<iostream>
using namespace std;
#define MAX(a, b) a > b? a:b
struct TreeNode{
int data;
TreeNode* child[2];
int size;
int height;
int count;
TreeNode(int val){
data = val;
size = count = height = 1;
child[0] = child[1] = NULL;
}
void Update(){
size = count;
height = 1;
if (child[0]){
size += child[0]->size;
height = MAX(height, 1 + child[0]->height);
}
if (child[1]){
size += child[1]->size;
height = MAX(height, 1 + child[1]->height);
}
}
}; struct AVL{
TreeNode* root;
AVL() :root(NULL){}; int GetHeight(TreeNode* node){
if (!node)
return 0;
return node->height;
}
int GetSize(TreeNode* node){
if (!node)
return 0;
return node->size;
} //注意这里使用指针的引用
void Rotate(TreeNode*& node, int dir){
TreeNode* ch = node->child[dir];
node->child[dir] = ch->child[!dir];
ch->child[!dir] = node;
node->Update();
node = ch;
}
void Maintain(TreeNode*& node){
if (!node){
return;
}
int bf = GetHeight(node->child[0]) - GetHeight(node->child[1]);
if (bf >= -1 && bf <= 1){
return;
}
if (bf == 2){
int bf2 = GetHeight(node->child[0]->child[0]) - GetHeight(node->child[0]->child[1]);
if (bf2 == 1){ //左左旋转
Rotate(node, 0);
}
else if (bf2 == -1){ //左右旋转
Rotate(node->child[0], 1);
Rotate(node, 0);
}
}
else if (bf == -2){
int bf2 = GetHeight(node->child[1]->child[0]) - GetHeight(node->child[1]->child[1]);
if (bf2 == 1){ //右左旋转
Rotate(node, 1);
}
else if (bf2 == -1){ //右右旋转
Rotate(node->child[1], 0);
Rotate(node, 1);
}
} }
void Insert(TreeNode*& node, int val){
if (!node){
node = new TreeNode(val);
}
else if (node->data == val){
node->count++;
}
else{
int dir = node->data < val;
Insert(node->child[dir], val);
}
//维持树的平衡
Maintain(node);
//更新节点
node->Update();
} //注意参数为指针的引用
void Delete(TreeNode*& node, int val){
if (!node){
return;
}else if (node->data == val){
if (node->child[0] && node->child[1]){
int dir = GetHeight(node->child[0]) < GetHeight(node->child[1]);
//将子树中较高的那棵,旋转
Rotate(node, dir);
//递归调用delete,直到叶子节点才进行真正的删除
Delete(node->child[! dir], val);
}
else{
TreeNode* tmp_node = NULL;
if (node->child[0]){
tmp_node = node->child[0];
}
else if (node->child[1]){
tmp_node = node->child[1];
}
delete node;
node = tmp_node; //使用引用
}
}
else{
int dir = node->data < val;
Delete(node->child[dir], val);
}
Maintain(node); //维持平衡
node->Update(); //更新节点
}
int GetKth(TreeNode* node, int k){
while (node){
if (! node->child[0]){
if (k <= node->count){
return node->data;
}
else{
k -= node->count;
node = node->child[1];
}
}
else{
if (node->child[0]->size < k && node->child[0]->size + node->count >= k){
return node->data;
}
else if (node->child[0]->size > k){
node = node->child[0];
}
else{
k -= (node->child[0]->size + node->count);
node = node->child[1];
}
}
}
return -1;
}
};

参考: 
平衡树 balanced binary tree (AVL tree)  
AVL树 模板

AVL 平衡树的更多相关文章

  1. 实现Avl平衡树

    实现Avl平衡树   一.介绍 AVL树是一种自平衡的二叉搜索树,它由Adelson-Velskii和 Landis于1962年发表在论文<An algorithm for the organi ...

  2. Python与数据结构[3] -> 树/Tree[2] -> AVL 平衡树和树旋转的 Python 实现

    AVL 平衡树和树旋转 目录 AVL平衡二叉树 树旋转 代码实现 1 AVL平衡二叉树 AVL(Adelson-Velskii & Landis)树是一种带有平衡条件的二叉树,一棵AVL树其实 ...

  3. 数据结构学习-AVL平衡树

    环境:C++ 11 + win10 IDE:Clion 2018.3 AVL平衡树是在BST二叉查找树的基础上添加了平衡机制. 我们把平衡的BST认为是任一节点的左子树和右子树的高度差为-1,0,1中 ...

  4. AVL平衡树的插入例程

    /* **AVL平衡树插入例程 **2014-5-30 11:44:50 */ avlTree insert(elementType X, avlTree T){ if(T == NULL){ T = ...

  5. AVL平衡树(非指针实现)

    看了网上三四篇博客,学习了AVL树维护平衡的方式.但感觉他们给出的代码都有一点瑕疵或者遗漏,懂得了思想之后,花了一些时间把他们几篇的长处结合起来,没有使用指针,实现了一下.每个小逻辑功能都抽象成了函数 ...

  6. 数据结构——AVL平衡树

    1.是二叉搜索树(Binary Search Tree) 2.树和所有左右子树高度之差为-1,0,1 平衡因子(balance factor) =右子树高度-左子树高度 平衡化旋转: 1.从插入位置向 ...

  7. My implementation of AVL tree

    C++实现的avl平衡树 #include <stdlib.h> #include <time.h> #include <string.h> #include &l ...

  8. [SinGuLaRiTy] 平衡树

    [SinGuLaRiTy-1009] Copyright (c) SinGuLaRiTy 2017. All Rights Reserved. 二叉查找树 二叉查找树是指具有下列性质的非空二叉树: ⑴ ...

  9. 数据结构--Avl树的创建,插入的递归版本和非递归版本,删除等操作

    AVL树本质上还是一棵二叉搜索树,它的特点是: 1.本身首先是一棵二叉搜索树.   2.带有平衡条件:每个结点的左右子树的高度之差的绝对值最多为1(空树的高度为-1).   也就是说,AVL树,本质上 ...

随机推荐

  1. contiki 无线测试 1个中心节点 13个从节点

    1 DATA recv '25.00 degres' from 2423:7c02:5525:4f2b2 DATA recv '27.71 degres' from 24f7:af03:5525:4f ...

  2. si4438 cca 侦听

    /* set GPIO0 for RSSI interrupt / CCA */txbuf[0] = CMD_GPIO_PIN_CFG;txbuf[1] = 27; /* GPIO[0] = 27: ...

  3. rails rake和示例

    一篇看到的讲解得不错的文章 http://blog.csdn.net/clskkk2222/article/details/6735365 这里还有一些例子: Rake Documentation R ...

  4. Comet——随着AJAX技术兴起而产生的新技术

    不得不说Ajax确实是一个好东西,由它的出现使得WEB端新技术不断产生,Comet就属于这么一个技术,这个技术有时叫做反向AJAX,有时叫做服务器"推"技术,嗯,不要被牛逼闪闪的名 ...

  5. Java实现一个简单的缓存方法

    缓存是在web开发中经常用到的,将程序经常使用到或调用到的对象存在内存中,或者是耗时较长但又不具有实时性的查询数据放入内存中,在一定程度上可以提高性能和效率.下面我实现了一个简单的缓存,步骤如下. 创 ...

  6. Spring 4 官方文档学习(五)核心技术之SpEL

    题外话 官方文档用evaluate这个单词来描述从表达式中获得实际内容的过程.如果直译的话,应该是评估.估值之类的意思.个人以为翻译成解析更易懂,但parse已经是解析了,为了避免冲突,就只好保留了e ...

  7. e551. 精简的Applet

    Every applet must subclass Applet. import java.applet.*; import java.awt.*; public class BasicApplet ...

  8. DB2多行转一行【XML方式】

    分组然后合并,然后去除XML标签 SELECT replace(replace(replace(xml2clob(xmlagg(xmlelement(name A, [字段]))),'</A&g ...

  9. circRNA 序列提取中的难点

    在预测circRNA时,都是检测breakpoint 处的reads 数,最后给出的环状RNA的ID 都是诸如 chr14:106994222-107183708 这样的形式,给出了起始和终止位置: ...

  10. 你是否有遇到过某个实体类字段(属性)过多的情况,不想每次点的话戳进来(C# 反射)

    贴上一段代码: bureaucraticEntities apply = new bureaucraticEntities(); Type tapp= app.GetType(); Type ttmp ...