上次我们已经实现了普通的二叉查找树。利用二叉查找树,可以用O(logN)高度的树状结构存储和查找数据,提高了存储和查找的效率。

然而,考虑一种极端情形:依次插入1,2,3,4,5,6,7,8,9九个元素,形成的二叉查找树实际上是一个线性表,每层只有一个元素,元素数与层数相同。

事实上,不只这一种情形。在很多情况下,都有可能出现这种结构。这样一来,二叉查找树就失去了它存在的意义。于是,我们考虑在每次插入和删除元素时,对树的结构进行一些检查和维护,使其每层的元素数尽可能多,从而尽可能降低层数,我们称为平衡

AVL树即是对二叉查找树的一种平衡方法。其要求为,每个结点的左右子树高度差不超过1.

于是,我们需要在结点的结构中增加一个height成员,每次插入或删除时,更新这个成员,并检测是否满足上述条件,若不满足,则需调整树的结构。

不难想到,如果每次违背上述条件时,都及时进行调整,那么违背条件的情况一定是左右子树高度差为2的情况。所以,对左右子树高度差为2这种特殊情形进行分析。

我们可以将这种情况进行分类,分为(简记):左左,左右,右左,右右四种。

以第一种情况为例,形象地说,左左即左子树比右子树高度大2,且左子树偏向左侧。如果用二叉树的属性来描述,即左子树的左子树高度大于右子树高度。

其他三种情况类似,不再赘述。

由对称性,左左和右右实际为一种情况,左右和右左为另一种情况。所以,算法的设计,只需要考虑这两种情况。具体代码则需要分四种情况来实现。

对于左左,我们采取单旋转(Single Rotate)的方法来进行调整。如图:

而对于左右,我们采取双旋转(Double Rotate),即两次单旋转的方法来调整:先对左子树进行右侧单旋转(即上述图示旋转方向的反方向),再对原结点进行左侧单旋转。这里不再说明这种方法为何正确。

代码如下:

// AvlTree.h

#include <stdio.h>
#include <stdlib.h> struct _AvlNode;
typedef struct _AvlNode AvlNode;
typedef AvlNode *Position;
typedef AvlNode *AvlTree; AvlTree MakeEmpty(AvlTree T);
Position Find(ElementType X, AvlTree T);
Position FindMin(AvlTree T);
Position FindMax(AvlTree T);
AvlTree Insert(ElementType X, AvlTree T);
AvlTree Delete(ElementType X, AvlTree T);
ElementType Retrieve(Position P);

  

// AvlTree.c

#include "AvlTree.h"

struct _AvlNode
{
ElementType Element;
AvlTree Left;
AvlTree Right;
int Height;
}; int Height(AvlTree T)
{
return T ? T->Height : 0;
} AvlTree MakeEmpty(AvlTree T)
{
if (T != NULL)
{
MakeEmpty(T->Left);
MakeEmpty(T->Right);
free(T);
}
return NULL;
} Position Find(ElementType X, AvlTree T)
{
if (T == NULL)
return NULL;
else if (T->Element < X)
return Find(X, T->Right);
else if (T->Element > X)
return Find(X, T->Left);
else
return T;
} Position FindMin(AvlTree T)
{
if (T != NULL)
while (T->Left != NULL)
T = T->Left;
return T;
} Position FindMax(AvlTree T)
{
if (T != NULL)
while (T->Right != NULL)
T = T->Right;
return T;
} AvlTree SingleRotateWithLeft(AvlTree T)
{
AvlTree temp;
temp = T->Left;
T->Left = temp->Right;
temp->Right = T;
temp->Height = Height(temp->Left) > Height(temp->Right) ? Height(temp->Left) + 1 : Height(temp->Right) + 1;
T->Height = Height(T->Left) > Height(T->Right) ? Height(T->Left) + 1 : Height(T->Right) + 1;
return temp;
} AvlTree SingleRotateWithRight(AvlTree T)
{
AvlTree temp;
temp = T->Right;
T->Right = temp->Left;
temp->Left = T;
temp->Height = Height(temp->Right) > Height(temp->Left) ? Height(temp->Right) + 1 : Height(temp->Left) + 1;
T->Height = Height(T->Right) > Height(T->Left) ? Height(T->Right) + 1 : Height(T->Left) + 1;
return temp;
} AvlTree DoubleRotateWithLeft(AvlTree T)
{
T->Left = SingleRotateWithRight(T->Left);
return SingleRotateWithLeft(T);
} AvlTree DoubleRotateWithRight(AvlTree T)
{
T->Right = SingleRotateWithLeft(T->Right);
return SingleRotateWithRight(T);
} AvlTree Insert(ElementType X, AvlTree T)
{
if (T == NULL)
{
if ((T = (AvlTree)malloc(sizeof(AvlNode))) == NULL)
{
printf("Error! Out of space! \n");
return NULL;
}
else
{
T->Element = X;
T->Left = T->Right = NULL;
T->Height = 0;
}
}
else if (X < T->Element)
{
T->Left = Insert(X, T->Left);
if (Height(T->Left) - Height(T->Right) == 2)
{
if (X < T->Left->Element)
T = SingleRotateWithLeft(T);
else
T = DoubleRotateWithLeft(T);
}
}
else if (X > T->Element)
{
T->Right = Insert(X, T->Right);
if (Height(T->Right) - Height(T->Left) == 2)
{
if (X < T->Right->Element)
T = DoubleRotateWithRight(T);
else
T = SingleRotateWithRight(T);
}
}
T->Height = Height(T->Left) > Height(T->Right) ? Height(T->Left) + 1 : Height(T->Right) + 1;
return T;
} AvlTree Delete(ElementType X, AvlTree T)
{
Position temp;
if (T == NULL)
{
printf("Error! No such node! \n");
return NULL;
}
if (X < T->Element)
{
T->Left = Delete(X, T->Left);
T->Height = Height(T->Left) > Height(T->Right) ? Height(T->Left) + 1 : Height(T->Right) + 1;
if (Height(T->Right) - Height(T->Left) == 2)
{
if (Height(T->Right->Left) > Height(T->Right->Right))
T = DoubleRotateWithRight(T);
else
T = SingleRotateWithRight(T);
}
}
else if (X > T->Element)
{
T->Right = Delete(X, T->Right);
T->Height = Height(T->Left) > Height(T->Right) ? Height(T->Left) + 1 : Height(T->Right) + 1;
if (Height(T->Left) - Height(T->Right) == 2)
{
if (Height(T->Left->Left) < Height(T->Left->Right))
T = DoubleRotateWithLeft(T);
else
T = SingleRotateWithLeft(T);
}
}
else
{
if (T->Left && T->Right)
{
temp = FindMin(T->Right);
T->Element = temp->Element;
T->Right = Delete(T->Element, T->Right);
}
else
{
temp = T;
if(T->Left == NULL)
T = T->Right;
else if (T->Right == NULL)
T = T->Left;
free(temp);
}
}
return T;
} ElementType Retrieve(Position P)
{
return P->Element;
}

  

《数据结构与算法分析——C语言描述》ADT实现(NO.04) : AVL树(AVL-Tree)的更多相关文章

  1. 《数据结构与算法分析——C语言描述》ADT实现(NO.00) : 链表(Linked-List)

    开始学习数据结构,使用的教材是机械工业出版社的<数据结构与算法分析——C语言描述>,计划将书中的ADT用C语言实现一遍,记录于此.下面是第一个最简单的结构——链表. 链表(Linked-L ...

  2. 数据结构与算法分析——C语言描述 第三章的单链表

    数据结构与算法分析--C语言描述 第三章的单链表 很基础的东西.走一遍流程.有人说学编程最简单最笨的方法就是把书上的代码敲一遍.这个我是头文件是照抄的..c源文件自己实现. list.h typede ...

  3. 最小正子序列(序列之和最小,同时满足和值要最小)(数据结构与算法分析——C语言描述第二章习题2.12第二问)

    #include "stdio.h" #include "stdlib.h" #define random(x) (rand()%x) void creat_a ...

  4. C语言学习书籍推荐《数据结构与算法分析:C语言描述(原书第2版)》下载

    维斯 (作者), 冯舜玺 (译者) <数据结构与算法分析:C语言描述(原书第2版)>内容简介:书中详细介绍了当前流行的论题和新的变化,讨论了算法设计技巧,并在研究算法的性能.效率以及对运行 ...

  5. 《数据结构与算法分析-Java语言描述》 分享下载

    书籍信息 书名:<数据结构与算法分析-Java语言描述> 原作名:Data Structures and Algorithm Analysis in Java 作者: 韦斯 (Mark A ...

  6. 读书笔记:《数据结构与算法分析Java语言描述》

    目录 第 3 章 表.栈和队列 3.2 表 ADT 3.2.1 表的简单数组实现 3.2.2 简单链表 3.3 Java Collections API 中的表 3.3.1 Collection 接口 ...

  7. 《数据结构与算法分析——C语言描述》ADT实现(NO.03) : 二叉搜索树/二叉查找树(Binary Search Tree)

    二叉搜索树(Binary Search Tree),又名二叉查找树.二叉排序树,是一种简单的二叉树.它的特点是每一个结点的左(右)子树各结点的元素一定小于(大于)该结点的元素.将该树用于查找时,由于二 ...

  8. 《数据结构与算法分析——C语言描述》ADT实现(NO.05) : 散列(Hash)

    散列(Hash)是一种以常数复杂度实现查找功能的数据结构.它将一个关键词Key,通过某种映射(哈希函数)转化成索引值直接定位到相应位置. 实现散列有两个关键,一是哈希函数的选择,二是冲突的处理. 对于 ...

  9. 《数据结构与算法分析——C语言描述》ADT实现(NO.01) : 栈(Stack)

    这次的数据结构是一种特殊的线性表:栈(Stack) 栈的特点是后入先出(LIFO),可见的只有栈顶的一个元素. 栈在程序中的地位非常重要,其中最重要的应用就是函数的调用.每次函数调用时都会创建该函数的 ...

随机推荐

  1. PHP算法之IP 地址无效化

    给你一个有效的 IPv4 地址 address,返回这个 IP 地址的无效化版本. 所谓无效化 IP 地址,其实就是用 "[.]" 代替了每个 ".". 示例 ...

  2. Android Studio 配置快速生成模板代码

    前言 Android studio 有提供快速生成模板代码的功能,其实这个功能也可以自定义配置.此篇博客将讲解如何使用此功能 进入Settings 选择 Editor > Live Templa ...

  3. 云时代IDC自动化运维的几大神器

    云时代IDC自动化运维的几大神器 2016年09月18日 10:27:41 天府云创 阅读数:1715   版权声明:本文为EnweiTech原创文章,未经博主允许不得转载. https://blog ...

  4. ArrayList 扩容

    处理容量是0, 第一次add的时候扩充到10 int newCapacity = oldCapacity + (oldCapacity >> 1); // 扩容50% 变成 1.5倍 第二 ...

  5. .net core 3.0 发布 500.0 - ANCM In-Process Handler Load Failure 错误

    .net core 3.0 发布后报500.0错误  原因: 发布设置和下载的Runtime SDK不匹配 https://dotnet.microsoft.com/download/dotnet-c ...

  6. SCRIPT7002: XMLHttpRequest: 网络错误 0x2ef3的解决方法

    最近在使用jquery easyui datagrid 对页面布局,发现有时在IE下会接收不到数据并报错: SCRIPT7002: XMLHttpRequest: 网络错误 0x2ef3, 由于出现错 ...

  7. VS2010-MFC(对话框:颜色对话框)

    转自:http://www.jizhuomi.com/software/177.html 颜色对话框大家肯定也不陌生,我们可以打开它选择需要的颜色,简单说,它的作用就是用来选择颜色.MFC中提供了CC ...

  8. 资源-Java:Java资源列表

    ylbtech-资源-Java:Java资源列表 1. 开发软件返回顶部 1.Eclipse https://www.eclipse.org/ 2.IntelliJ IDEA https://www. ...

  9. System.Web.Mvc.ControllerBase.cs

    ylbtech-System.Web.Mvc.ControllerBase.cs 1.程序集 System.Web.Mvc, Version=5.2.3.0, Culture=neutral, Pub ...

  10. 深入分析Service启动、绑定过程

    Service是Android中一个重要的组件,它没有用户界面,可以运行在后太做一些耗时操作.Service可以被其他组件启动,甚至当用户切换到其他应用时,它仍然可以在后台保存运行.Service 是 ...