欢迎探讨,如有错误敬请指正

如需转载,请注明出处http://www.cnblogs.com/nullzx/

1. AVL定义

AVL树是一种改进版的搜索二叉树。对于一般的搜索二叉树而言,如果数据恰好是按照从小到大的顺序或者从大到小的顺序插入的,那么搜索二叉树就对退化成链表,这个时候查找,插入和删除的时间都会上升到O(n),而这对于海量数据而言,是我们无法忍受的。即使是一颗由完全随机的数据构造成的搜索二叉树,从统计角度去分析,在进行若甘次的插入和删除操作,这个搜索二叉树的高度也不能令人满意。这个时候大家就希望能有一种二叉树解决上述问题。这个时候就出现平衡搜索二叉树,它的基本原理就是在插入和删除的时候,根据情况进行调整,以降低二叉树的高度。平衡搜索二叉树典型代表就是AVL树和红黑树。

AVL树:任何一个节点的左子支高度与右子支高度之差的绝对值不超过1。需要我们注意的是,AVL树定义不是说从根节点到叶子节点的最短距离比最长短距离大1。

上图就是一颗AVL树,从根节点到叶子节点的最短距离是5,最长距离是9。

2. 旋转的定义

因为每种书中对旋转的定义不一致,所以我们有必要在这里特此说明一下

以某一个节点为轴,它的左子枝顺时针旋转,作为新子树的根,我们称之为顺时针旋转(clockwise)或者右旋转。

同理,以某一个节点为轴,它的右子枝逆针旋转,作为新子树的根,我们称之为逆时针旋转(anticlockwise)或者左旋转。

3. AVL插入操作

AVL树的插入操作首先会按照普通搜索二叉树的插入操作进行,当插入一个数据后,我们会沿着插入数据时所经过的的节点回溯,回溯的过程中会判回溯路径中的每个节点的左子支高度与右子支高度之差的绝对值是否超过1,如果超过1我们就进行调整,调整的目的是使得该节点满足AVL树的定义。调整的情况可以分为以下四旋转操作,旋转操作可以降低树的高度,同时不改变搜索二叉树的性质(即任何一个节点左子支中的全部节点小于该节点,右子支的全部节点大于该节点)。

3.1 情况1

节点X左子支比右子支高度大2,且插入的节点位于X的左孩子节点XL的左子支上

3.2 情况2

节点X右子支比左子支高度大2,且插入的节点位于节点X右孩子节点XR的右子支上

3.3 情况3

节点X左子支比右子支高度大2,且插入的节点位于节点X左孩子节点XL的右子支上

3.4 情况4

节点X左子支比右子支高度大2,且插入的节点位于节点X左孩子节点XL的右子支上

4. AVL删除操作

AVL树的删除操作和插入操作一样,首先会按照普通搜索二叉树的删除操作进行,当删除一个数据后,和插入操作一样,我们通常采取的策略是沿着删除数据时所经过的的节点回溯,回溯的过程中会判断该节点的左子支高度与右子支高度之差的绝对值是否超过1(或者说大2),如果超过1,我们就进行调整,调整的目的是使得该节点满足AVL树的定义。调整的情况可以分为四种,和插入过程完全一样,这里不在赘述。

5. C语言实现

5.1节点定义

AVLtree.h文件中的内容

#ifndef __AVLTREE_H__
#define __AVLTREE_H__
typedef struct Node{
int height; //该节点作为子树时的高度
int data; //表示每个节点存贮的数据
Node* left;
Node* right;
}Node, *AVLtree;
//AVLtree 表示Node*
//AVLtree* 就表示Node** int Insert(AVLtree* T, int D);
int Delete(AVLtree* T, int D);
int Find(AVLtree T, int x);
int Destroy(AVLtree* T); //下面两个遍历函数主要用于测试
void InOredrTraverse(AVLtree T);
void PreOredrTraverse(AVLtree T);
#endif

5.2代码实现

AVLtree.cpp文件中的内容

#include"AVLtree.h"
#include<stdlib.h>
#include<stdio.h> #define MAX(x1,x2) ((x1) > (x2) ? (x1) : (x2)) //一下函数用于辅助实现插入删除操作,作用域于仅限于AVLtree.cpp
static void PostOrderTraverse(AVLtree T);
static int GetHeight(AVLtree T);
static void LeftRotate(AVLtree* T);
static void RightRotate(AVLtree* T);
static int FindMin(AVLtree T); //返回值用于表示插入是否成功,-1表示失败(说明树中已包含该数据),0表示成功
int Insert(AVLtree* T,int D){
//参数检查
if(T == NULL){
return -1;
} //找到插入的位置
if(*T == NULL){
*T = (Node*)malloc(sizeof(Node));
(*T)->data = D;
(*T)->height = 1;
(*T)->left = NULL;
(*T)->right = NULL;
return 0;
}else{
//树中已存在该数据
if(D == (*T)->data){
return -1;
}else
if(D > (*T)->data){//在右子树中插入
if(Insert(&(*T)->right,D) == -1){
return -1;
} //插入后,当回溯到该节点进行检查,如果不满足平衡条件,则调整
//因为是在右子支中插入,如果高度只差等于2,只可能是右子支比左子支高
if(GetHeight((*T)->right) - GetHeight((*T)->left) == 2){
if(D > (*T)->right->data){
LeftRotate(T);//对应情况2,左旋
}else{//对应情况4,先右旋再左旋
RightRotate(&(*T)->right);
LeftRotate(T);
}
}
}else
if(D < (*T)->data){//在左子树中插入
if(Insert(&(*T)->left,D)){
return -1;
} if(GetHeight((*T)->left) - GetHeight((*T)->right) == 2){
if(D < (*T)->left->data){
RightRotate(T);//对应情况1,左旋
}else{//对应情况3,先右旋再左旋
LeftRotate(&(*T)->left);
RightRotate(T);
}
}
}
} //更新当前节点的高度
(*T)->height = MAX(GetHeight((*T)->left),GetHeight((*T)->right))+1;
return 0;
} //返回-1表示,树中没有该数据,删除失败,
int Delete(AVLtree* T,int D){
static Node* tmp;
if(T == NULL){
return -1;
} if(*T == NULL){//树为空,或者树中没有该数据
return -1;
}else{
//找到要删除的节点
if(D == (*T)->data){
//删除的节点左右子支都不为空,一定存在前驱节点
if((*T)->left != NULL && (*T)->right != NULL){
D = FindMin((*T)->right);//找后继替换
(*T)->data = D;
Delete(&(*T)->right,D);//然后删除后继节点,一定成功
//在右子支中删除,删除后有可能左子支比右子支高度大2
if(GetHeight((*T)->left)-GetHeight((*T)->right) == 2){
//判断哪一个左子支的的两个子支哪个比较高
if(GetHeight((*T)->left->left) >= GetHeight((*T)->left->right)){
RightRotate(T);
}else{
LeftRotate(&(*T)->left);
RightRotate(T);
}
}
}else
if((*T)->left == NULL){//左子支为空
tmp = (*T);
(*T) = tmp->right;
free(tmp);
return 0;
}else
if((*T)->right == NULL){//右子支为空
tmp = (*T);
(*T) = tmp->left;
free(tmp);
return 0;
}
}else
if(D > (*T)->data){//在右子支中寻找待删除的节点
if(Delete(&(*T)->right,D) == -1){
return -1;//删除失败,不需要调整,直接返回
}
if(GetHeight((*T)->left)-GetHeight((*T)->right) == 2){
if(GetHeight((*T)->left->left) >= GetHeight((*T)->left->right)){
RightRotate(T);
}else{
LeftRotate(&(*T)->left);
RightRotate(T);
}
}
}else
if(D < (*T)->data){//在左子支中寻找待删除的节点
if(Delete(&(*T)->left,D) == -1){
return -1;
}
if(GetHeight((*T)->right) - GetHeight((*T)->left) == 2){
if(GetHeight((*T)->right->right) >= GetHeight((*T)->right->left)){
LeftRotate(T);
}else{
RightRotate(&(*T)->right);
LeftRotate(T);
}
}
}
}
//更新当前节点的高度
(*T)->height = MAX(GetHeight((*T)->left),GetHeight((*T)->right))+1;
return 0;
} int Find(AVLtree T,int x){
while(T != NULL){
if(T->data == x){
return 0;
}else
if(x > T->data){
T = T->right;
}else{
T = T->left;
}
}
return -1;
} int Destroy(AVLtree* T){
if(T == NULL){
return -1;
} PostOrderTraverse(*T);
*T = NULL;
return 0;
} void InOredrTraverse(AVLtree T){
if(T != NULL){
InOredrTraverse(T->left);
printf("%3d ",T->data);
InOredrTraverse(T->right);;
}
} void PreOredrTraverse(AVLtree T){
if(T != NULL){
printf("%3d:%2d(%3d,%3d)\n",T->data,T->height,
T->left == NULL?-1:T->left->data,
T->right == NULL?-1:T->right->data
);
PreOredrTraverse(T->left);
PreOredrTraverse(T->right);
}
} static void PostOrderTraverse(AVLtree T){
if(T != NULL){
PostOrderTraverse(T->left);
PostOrderTraverse(T->right);
free(T);
}
} //空数的高度为0
static int GetHeight(AVLtree T){
if(T == NULL){
return 0;
}else{
return T->height;
}
} static void LeftRotate(AVLtree* T){
Node *P,*R;
P = *T;
R = P->right;
P->right = R->left;
R->left = P;
*T = R; //旋转以后要更新节点的高度
P->height = MAX(GetHeight(P->left),GetHeight(P->right))+1;
R->height = MAX(GetHeight(R->left),GetHeight(R->right))+1;
} static void RightRotate(AVLtree* T){
Node *P,*L;
P = *T;
L = P->left;
P->left = L->right;
L->right = P;
*T = L;
//旋转以后要更新节点的高度
P->height = MAX(GetHeight(P->left),GetHeight(P->right))+1;
L->height = MAX(GetHeight(L->left),GetHeight(L->right))+1;
} static int FindMin(AVLtree T){
if(T == NULL){
return -1;
} while(T->left != NULL){
T = T->left;
}
return T->data;
}

6. Java语言泛型实现

package datastruct;

import java.util.Comparator;

public class AVLtree <E>{
private static class Node<E>{
int h;
E element;
Node<E> left;
Node<E> right;
//由于java中不像C语言那样有二级指针的概念,所以添加一个父类的引用,方便程序编写
Node<E> parent; public Node(E element, int h, Node<E> left, Node<E> right, Node<E> parent){
this.element = element;
this.h = h;
this.left = left;
this.right = right;
this.parent = parent;
}
} private Node<E> root;//指向伪根节点的引用
private int size = 0;//节点个数
Comparator<? super E> cmp;//节点大小的比较器 //如果调用了不带参数的构造函数,则使用该内部类作为比较器,
//但此时泛型E需要继承Comparable接口,否则运行时会抛出异常
private static class Cmp<T> implements Comparator<T>{
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public int compare(T e1, T e2) {
return ((Comparable)e1).compareTo(e2);
} } //带比较器的构造函数
public AVLtree(Comparator<? super E> cmp){
if(cmp == null){
throw new IllegalArgumentException();
}
this.cmp = cmp;
//创建一个伪根节点,该节点的右子支才是真正的AVL树的根
//使用伪根节点节点的目的是,对插入和删除操作递归的形式能够统一
root = new Node<E>(null, -1, null, null, null);
} //不带比较器的构造函数
public AVLtree(){
this.cmp = new Cmp<E>();
root = new Node<E>(null, -1, null, null, null);
} //如果树中节点有变动,从底向上逐级调用该函数,可以更新节点的高度
private int getHight(Node<E> x){
if(x == null){
return 0;
}else{
return x.h;
}
} //求某个节点作为根时,该子树的最小值
private E treeMin(Node<E> x){
while(x.left != null){
x = x.left;
}
return x.element;
} public int size(){
return size;
} //先根遍历,调试时使用
public void preorderTraverse(){
if(root != null){
preorderTraverse0(root.right);
}
} private void preorderTraverse0(Node<E> x){
if(x != null){
System.out.print(x.element+" ");
if(x.left != null){
System.out.print(x.left.element+" ");
}else{
System.out.print("null ");
} if(x.right != null){
System.out.print(x.right.element+" ");
}else{
System.out.print("null ");
}
System.out.println();
preorderTraverse0(x.left);
preorderTraverse0(x.right);
}
} //逆时针旋转(左旋),参数表示轴节点
private void antiClockwiseRotate(Node<E> X){
Node<E> P = X.parent;
Node<E> XR = X.right;
if(P.left == X){
P.left = XR;
}else{
P.right = XR;
}
XR.parent = P; X.right = XR.left;
if(XR.left != null){
XR.left.parent = X;
} XR.left = X;
X.parent = XR; //旋转后要更新这两个节点的高度
X.h = Math.max(getHight(X.left), getHight(X.right)) + 1;
XR.h = Math.max(getHight(XR.left), getHight(XR.right)) + 1;
} //顺时针旋转(右旋),参数表示轴节点
private void clockwistRotate(Node<E> X){
Node<E> P = X.parent;
Node<E> XL = X.left;
if(P.left == X){
P.left = XL;
}else{
P.right = XL;
}
XL.parent = P; X.left = XL.right;
if(XL.right != null){
XL.right.parent = X;
} XL.right = X;
X.parent = XL; //旋转后要更新这两个节点的高度
X.h = Math.max(getHight(X.left), getHight(X.right)) + 1;
XL.h = Math.max(getHight(XL.left), getHight(XL.right)) + 1;
} //
public void insert(E e){
insert0(root.right, e);
} private void insert0(Node<E> x, E e){
if(x == null){
root.right = new Node<E>(e, 1, null, null, root);//根节点
size++;
return;
} if(cmp.compare(e, x.element) > 0){
if(x.right != null){
insert0(x.right, e);
int lh = getHight(x.left);
int rh = getHight(x.right);
if(rh - lh == 2){
if(cmp.compare(e, x.right.element) > 0){
antiClockwiseRotate(x);
}else{
clockwistRotate(x.right);
antiClockwiseRotate(x);
}
}
}else{
size++;
x.right = new Node<E>(e, 1, null, null, x);
}
}else
if(cmp.compare(e, x.element) < 0){
if(x.left != null){
insert0(x.left, e);
int lh = getHight(x.left);
int rh = getHight(x.right);
if(lh - rh == 2){
if(cmp.compare(e, x.left.element) < 0){
clockwistRotate(x);
}else{
antiClockwiseRotate(x.left);
clockwistRotate(x);
}
}
}else{
size++;
x.left = new Node<E>(e, 1, null, null, x);
}
}else{
//元素已存在,我们用新的元素更新旧,
//compare返回值等于0,并不表示两个对象完全相等
x.element = e;
}
x.h = Math.max(getHight(x.left), getHight(x.right)) + 1;
} public boolean delete(E e){
return delete0(root.right, e);
} //返回值表示是否删除成功
private boolean delete0(Node<E> x, E e){
if(x == null){//没有找到待删除的元素
return false;
} if(cmp.compare(e, x.element) > 0){
boolean reval = delete0(x.right, e);
if(reval == false){
return false;
} int lh = getHight(x.left);
int rh = getHight(x.right);
if(lh - rh == 2){
if(getHight(x.left.left) > getHight(x.left.right)){
clockwistRotate(x);
}else{
antiClockwiseRotate(x.left);
clockwistRotate(x);
}
}
}else
if(cmp.compare(e, x.element) < 0){
boolean reval = delete0(x.left, e);
if(reval == false){
return false;
} int lh = getHight(x.left);
int rh = getHight(x.right);
if(rh - lh == 2){
if(getHight(x.right.right) > getHight(x.right.left)){
antiClockwiseRotate(x);
}else{
clockwistRotate(x.right);
antiClockwiseRotate(x);
}
}
}else{//找到待删除的元素
Node<E> P = x.parent; if(x.left == null){//左子支为空,可直接删除,在这一层一定不需要旋转
size--;
if(P.left == x){
P.left = x.right;
if(P.left != null){
P.left.parent = P;
}
}else{
P.right = x.right;
if(P.right != null){
P.right.parent = P;
}
}
}else
if(x.right == null){//右子支为空,可直接删除,在这一层一定不需要旋转
size--;
if(P.left == x){
P.left = x.left;
if(P.left != null){
P.left.parent = P;
}
}else{
P.right = x.left;
if(P.right != null){
P.right.parent = P;
}
}
}else{//找到待删除的节点,用后继节点代替,然后删除后继节点
E nextVal = treeMin(x.right);
x.element = nextVal;
delete0(x.right, nextVal);
int lh = getHight(x.left);
int rh = getHight(x.right);
if(lh - rh == 2){
if(getHight(x.left.left) > getHight(x.left.right)){
clockwistRotate(x);
}else{
antiClockwiseRotate(x.left);
clockwistRotate(x);
}
}
}
}
x.h = Math.max(getHight(x.left), getHight(x.right)) + 1;
return true;
} public static void main(String[] args){
AVLtree<Integer> avl = new AVLtree<Integer>();
/*可自行添加插入,删除操作进行测试*/
avl.insert(3);
avl.insert(5);
avl.insert(6);
avl.insert(7);
avl.insert(8);
avl.insert(9);
avl.preorderTraverse();
System.out.println();
System.out.println(avl.size()); avl.delete(7);
avl.delete(8);
avl.preorderTraverse();
System.out.println();
System.out.println(avl.size());
}
}

AVL树原理及实现(C语言实现以及Java语言实现)的更多相关文章

  1. AVL树原理及实现 +B树

    1. AVL定义 AVL树是一种改进版的搜索二叉树.对于一般的搜索二叉树而言,如果数据恰好是按照从小到大的顺序或者从大到小的顺序插入的,那么搜索二叉树就对退化成链表,这个时候查找,插入和删除的时间都会 ...

  2. AVL树(查找、插入、删除)——C语言

    AVL树 平衡二叉查找树(Self-balancing binary search tree)又被称为AVL树(AVL树是根据它的发明者G. M. Adelson-Velskii和E. M. Land ...

  3. Java语言编码规范 - Java语言编码规范(中文版)(http://doc.javanb.com/code-conventions-for-the-java-programming-language-zh/index.html)

      目录 1 介绍 1.1 为什么要有编码规范 1.2 版权声明 2 文件名 2.1 文件后缀 2.2 常用文件名 3 文件组织 3.1 Java源文件 3.1.1 开头注释 3.1.2 包和引入语句 ...

  4. Java 语言基础 (初识Java语言, 变量和数据类型, 运算符, 流程控制语句, 数组)

    初始 Java 语言 Java SE -- Java Platform, Standard Edition 是 Java 平台的基础 Java SE 以前称为 J2SE, 可以编写桌面应用和基于 we ...

  5. AVL树,红黑树,B-B+树,Trie树原理和应用

    前言:本文章来源于我在知乎上回答的一个问题 AVL树,红黑树,B树,B+树,Trie树都分别应用在哪些现实场景中? 看完后您可能会了解到这些数据结构大致的原理及为什么用在这些场景,文章并不涉及具体操作 ...

  6. 04-树5 Root of AVL Tree + AVL树操作集

    平衡二叉树-课程视频 An AVL tree is a self-balancing binary search tree. In an AVL tree, the heights of the tw ...

  7. AVL树(平衡二叉查找树)

    首先要说AVL树,我们就必须先说二叉查找树,先介绍二叉查找树的一些特性,然后我们再来说平衡树的一些特性,结合这些特性,然后来介绍AVL树. 一.二叉查找树 1.二叉树查找树的相关特征定义 二叉树查找树 ...

  8. Java语言概述

    1.1 基础知识 ·第一代语言 打孔机--纯机器语言 ·第二代语言 汇编 ·第三代语言 C.Pascal.Fortran面向过程的语言 C++面向过程/面向对象 Java跨平台的纯面向对象的语言 .N ...

  9. 概述java语言

    1.java语言是什么? java是一门面向对象的高级语言,它吸收了c++语言的各种优点,还摒弃了C++里难以理解的多继承和指针等概念,因此Java语言具有功能强大和简单易用两个特征. 2.java语 ...

随机推荐

  1. 《Web 前端面试指南》1、JavaScript 闭包深入浅出

    闭包是什么? 闭包是内部函数可以访问外部函数的变量.它可以访问三个作用域:首先可以访问自己的作用域(也就是定义在大括号内的变量),它也能访问外部函数的变量,和它能访问全局变量. 内部函数不仅可以访问外 ...

  2. shiro权限管理框架与springmvc整合

    shiro是apache下的一个项目,和spring security类似,用于用户权限的管理‘ 但从易用性和学习成本上考虑,shiro更具优势,同时shiro支持和很多接口集成 用户及权限管理是众多 ...

  3. [原][Docker]特性与原理解析

    Docker特性与原理解析 文章假设你已经熟悉了Docker的基本命令和基本知识 首先看看Docker提供了哪些特性: 交互式Shell:Docker可以分配一个虚拟终端并关联到任何容器的标准输入上, ...

  4. 【uwp】浅谈China Daily 中划词翻译的实现

    学习uwp开发也有一段时间了,最近上架了一个小应用(China Daily),现在准备将开发中所学到的一些东西拿出来跟大家分享交流一下. 先给出应用的下载链接:China Daily , 感兴趣的童鞋 ...

  5. gRPC源码分析1-SSL/TLS

    引子 前几天看到微信后台团队分享了TLS相关文章,正好gRPC里TLS数据加密是很重要的一块,于是整理出了这篇文章. 在gRPC里,如果仅仅是用来做后端微服务,可以考虑不加密.本文太长,先给个大纲. ...

  6. jquery实现下拉框多选

    一.说明 本文是利用EasyUI实现下拉框多选功能,在ComboxTree其原有的基础上对样式进行了改进,样式表已上传demo,代码如下 二.代码 <!DOCTYPE html PUBLIC & ...

  7. (资源整理)带你入门Spark

    一.Spark简介: 以下是百度百科对Spark的介绍: Spark 是一种与 Hadoop 相似的开源集群计算环境,但是两者之间还存在一些不同之处,这些有用的不同之处使 Spark 在某些工作负载方 ...

  8. XSS 前端防火墙 —— 整装待发

    到目前为止,我们把能用前端脚本防御 XSS 的方案都列举了一遍. 尽管看起来似乎很复杂累赘,不过那些是理论探讨而已,在实际中未必要都实现.我们的目标只是为了预警,能发现问题就行,并非要做到滴水不漏的程 ...

  9. FastClick 填坑及源码解析

    最近产品妹子提出了一个体验issue —— 用 iOS 在手Q阅读书友交流区发表书评时,光标点击总是不好定位到正确的位置: 如上图,具体表现是较快点击时,光标总会跳到 textarea 内容的尾部.只 ...

  10. zone.js - 暴力之美

    在ng2的开发过程中,Angular团队为我们带来了一个新的库 – zone.js.zone.js的设计灵感来源于Dart语言,它描述JavaScript执行过程的上下文,可以在异步任务之间进行持久性 ...