上次我们已经实现了普通的二叉查找树。利用二叉查找树,可以用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. xx市xx项目运维工作方案

    注:提供给各位正在做项目,或准备做项目的朋友,仅供参考,用于后期运维提供的方案模板.仅供参考. 因为直接从word复制,会有一些排版的问题.可以留邮箱. xx市xx项目运维工作方案 xx有限公司 20 ...

  2. linq语句,常用的查询,模糊查询,实体查询

    查询: //List是要查询的实体列表的集合 List.FindAll(n => n.NAME == NAME),    //NAME变量是要查询的条件 模糊查询 List.FindAll(s ...

  3. MySQL数据库的基本语法

    1.MySQL数据类型数值以及浮点型介绍 2.MySQL数据类型之字符串介绍 常用的有:char.varchar.text. 3.MySQL数据类型之时间类型介绍 常用的是:timestampt,将时 ...

  4. leetcode-第10周双周赛-5099验证回文字符串③

    题目描述: 方法:动态规划 class Solution: def isValidPalindrome(self, s: str, k: int) -> bool: def isKPalRec( ...

  5. Java 基础 - Exception和Error

    综述 Exception 和 Error 都是继承了 Throwable 类,在 Java 中只有 Throwable 类型的实例才可以被抛出(throw)或者捕获(catch),它是异常处理机制的基 ...

  6. BIO 详解

    调用者主动等待调用的结果 简介 早期的jdk中,采用BIO通信模式: 通常有一个acceptor(消费者) 去负责监听客户端的连接. 它接收到客户端的连接请求之后为每个客户端创建一个线程进行链路处理, ...

  7. thjinkphp 变量调试

    输出某个变量是开发过程中经常会用到的调试方法,除了使用php内置的var_dump和print_r之外,ThinkPHP框架内置了一个对浏览器友好的dump方法,用于输出变量的信息到浏览器查看. 大理 ...

  8. 爬虫-Requests 使用入门

    requests 的底层实现其实就是 urllib json在线解析工具 ---------------------------------------------- Linux alias命令用于设 ...

  9. hadoop镜像文件和编辑日志文件

    镜像文件和编辑日志文件 1)概念 namenode被格式化之后,将在/opt/module/hadoop-2.7.2/data/tmp/dfs/name/current目录中产生如下文件 edits_ ...

  10. 求1到n这n个整数间的异或值 (O(1)算法)

      问题:求1到n这n个整数间的异或值,即 1 xor 2 xor 3 ... xor n 记 f(x, y) 为x到y的所有整数的异或值. 对 f(2^k, 2^(k+1) -1) (注意文章中的  ...