C平衡二叉树(AVL)创建和删除
AVL是最先发明的自平衡二叉查找树算法。在AVL中任何节点的两个儿子子树的高度最大差别为一,所以它也被称为高度平衡树,n个结点的AVL树最大深度约1.44log2n。查找、插入和删除在平均和最坏情况下都是O(log n)。增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。
定义
用LH,EH,RH分别表示左子树高,等高,右子树高,即平衡因子1、0、-1
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define LH 1 // 左高
#define EH 0 // 等高
#define RH -1 // 右高 typedef struct TreeNode{
int data;
int bf;
struct TreeNode *left, *right;
}TreeNode;
旋转处理
左旋和右旋,记住“左逆右顺”就可以
/************************************************
* 对以*p为根的二叉排序树作右旋处理,处理之后p指向新的树根结点,
* A B
* / / \
* B 旋转后变为 C A
* / \ /
* C D D
* 即旋转处理之前的左子树的结点。
************************************************/
void r_rotate(TreeNode **p){
TreeNode *l = (*p)->left;
(*p)->left = l->right;
l->right = (*p);
*p = l;
}
/************************************************
* 对以*p为根的二叉排序树作左旋处理,处理之后p指向新的树根结点,
* A B
* \ / \
* B 旋转后变为 A D
* / \ \
* C D C
* 即旋转处理之前的右子树的结点。
************************************************/
void l_rotate(TreeNode **p){
TreeNode *r = (*p)->right;
(*p)->right = r->left;
r->left = (*p);
*p = r;
}
左平衡处理
所谓左平衡处理,就是某一根结点的左子树比右子树高,从而失去了平衡。
(1)插入时如果需要左平衡处理,根结点左子树根平衡因子只可能为LH和RH。
(2)删除和插入不同,根结点左子树根的平衡因子三种情况都可能出现,因为是删除根结点右子树中的结点从而引起左子树过高,在删除前,根结点左子树根的平衡因子是可以为EH的,此种情况同样是对根结点做简单右旋处理。
/************************************************
* 对*t所指结点为根的二叉树作左平衡处理
************************************************/
void left_balance(TreeNode **t){
TreeNode *l, *lr;
l = (*t)->left;
switch(l->bf){
case LH:
(*t)->bf = l->bf = EH;
r_rotate(t);
break;
case RH:
lr = l->right;
switch(lr->bf){
case LH:
(*t)->bf = RH;
l->bf = EH;
break;
case RH:
(*t)->bf = EH;
l->bf = LH;
break;
case EH:
(*t)->bf = l->bf = EH;
break;
}
lr->bf = EH;
l_rotate(&(*t)->left);
r_rotate(t);
break;
case EH: // 删除节点时用到
(*t)->bf = LH;
l->bf = RH;
r_rotate(t);
break;
}
}
右平衡处理
类似左平衡处理,所谓右平衡处理,就是某一根结点的右子树比左子树高,从而失去了平衡。
(1)插入时如果需要右平衡处理,根结点右子树根平衡因子只可能为LH和RH。
(2)删除和插入不同,根结点右子树根的平衡因子三种情况都可能出现,因为是删除根结点左子树中的结点从而引起右子树过高,在删除前,根结点右子树根的平衡因子是可以为EH的,此种情况同样是对根结点做简单左旋处理。
/************************************************
* 对*t所指结点为根的二叉树作右平衡处理
************************************************/
void right_balance(TreeNode **t){
TreeNode *r, *rl;
r = (*t)->right;
switch(r->bf){
case RH:
(*t)->bf = r->bf = EH;
l_rotate(t);
break;
case LH:
rl = r->left;
switch(rl->bf){
case RH:
(*t)->bf = LH;
r->bf = EH;
break;
case LH:
(*t)->bf = EH;
r->bf = RH;
break;
case EH:
(*t)->bf = r->bf = EH;
break;
}
rl->bf = EH;
r_rotate(&(*t)->right);
l_rotate(t);
break;
case EH: // 删除节点时用到
(*t)->bf = RH;
r->bf = LH;
l_rotate(t);
break;
}
}
插入处理
在插入一个元素时,总是插入在一个叶子结点上。采用递归插入,也就是不断搜索平衡二叉树,找到一个合适的插入点(相同关键字不插入)。插入后,引起的第一个不平衡的子树的根结点,一定是在查找路径上离该插入点最近的。
/************************************************
* 若在平衡的二叉排序树t中不存在和e有相同关键字的结点,则插入一个
* 数据元素为e的新结点,并返回true,否则返回false。若因插入而使二叉排序树
* 失去平衡,则作平衡旋转处理,布尔变量taller反映t长高与否
************************************************/
bool insertAVL(TreeNode **t,int e,bool *taller){
if( ! *t ){
*t = (TreeNode *)malloc(sizeof(TreeNode));
(*t)->data = e;
(*t)->left = (*t)->right = NULL;
(*t)->bf = EH;
*taller = true;
}
else{
if( e == (*t)->data ){
*taller = false;
return false;
}
if( e < (*t)->data ){ // 在左子树中查找插入点
if( ! insertAVL(&(*t)->left,e,taller)){ // 左子树插入失败
return false;
}
if( *taller ){ // 左子树插入成功,且树增高
switch( (*t)->bf ){
case LH: // 原来t的左子树高于右子树
left_balance(t);// 左平衡处理
*taller = false;
break;
case EH: // 原来t的左子树和右子树等高
(*t)->bf = LH; // 现在左子树高
*taller = true; // 整棵树增高
break;
case RH: // 原来t的右子树高
(*t)->bf = EH; // 现在等高
*taller = false;// 树未增高
break;
}
}
}
else{ // 在右子树中查找插入点
if( ! insertAVL(&(*t)->right,e,taller)){ // 右子树插入失败
return false;
}
if( *taller ){ // 右子树插入成功,且树增高
switch( (*t)->bf ){
case RH: // 原来t的右子树高
right_balance(t);// 右平衡处理
*taller = false;
break;
case EH: // 原来t的左子树和右子树等高
(*t)->bf = RH; // 现在右子树高
*taller = true;
break;
case LH: // 原来t的左子树高
(*t)->bf = EH; // 现在等高
*taller = false;
break;
}
}
}
}
return true;
}
删除处理
删除和插入不同的是,删除的结点不一定是叶子结点,可能是树中的任何一个结点。
在操作二叉查找树时,我们知道删除的结点可能有三种情况:(1)为叶子结点(2)左子树或右子树有一个为空(3)左右子树都不空。
对第三种情况的处理这里我们采用删除前驱的方式。递归删除,判断删除后树是否“变矮”了,然后进行相应的处理。对(1)(2)中情况,很好处理,树的确是“变矮”了。对于第(3)种情况,我们不能直接找到前驱结点,然后把数据拷贝到原本要删除的根结点,最后直接删除前驱结点。因为这么做,我们无法判断原先根结点子树高度的变化情况。所以我们在找到前驱结点后,不是直接删除,而是采用在根结点左子树中递归删除前驱的方式。
/************************************************
* 若在平衡的二叉排序树t中存在和e有相同关键字的结点,则删除
* 并返回true,否则返回false。若因删除而使二叉排序树
* 失去平衡,则作平衡旋转处理,布尔变量lower反映t变矮与否
************************************************/
bool deleteAVL(TreeNode **t,int key,bool *lower){
if( ! *t ) return false;
TreeNode *q = NULL;
if( key == (*t)->data ){
if( NULL == (*t)->left ){ // 左子树为空,直接连接右节点
q = (*t);
(*t) = q->right;
free(q);
*lower = true;
}
else if( NULL == (*t)->right ){ // 右子树为空,直接连接左节点
q = (*t);
(*t) = q->left;
free(q);
*lower = true;
}
else{
q = (*t)->left;
while( q->right ){
q = q->right;
}
(*t)->data = q->data;
deleteAVL( &(*t)->left,q->data,lower); // 在左子树中递归删除前驱节点
}
}
else if( key < (*t)->data ){
if( !deleteAVL( &(*t)->left,key,lower) ){
return false;
}
if( *lower ){
switch( (*t)->bf ){
case LH:
(*t)->bf = EH;
*lower = true;
break;
case EH:
(*t)->bf = RH;
*lower = false;
break;
case RH:
right_balance(t);
if( EH == (*t)->right->bf ){
*lower = false;
}
else{
*lower = true;
} break;
}
}
}
else{
if( !deleteAVL( &(*t)->right,key,lower) ){
return false;
}
if( *lower ){
switch( (*t)->bf ){
case RH:
(*t)->bf = EH;
*lower = true;
break;
case EH:
(*t)->bf = LH;
*lower = false;
break;
case LH:
left_balance(t);
if( EH == (*t)->left->bf ){
*lower = false;
}
else{
*lower = true;
} break;
}
}
}
return true;
}
遍历和查找
/************************************************
* 在*t所指平衡二叉树中递归查找等于key的数据元素,
* 若查找成功,则返回true
************************************************/
bool searchAVL(TreeNode *t,int key,TreeNode *f,TreeNode **p){
if( !t ){
*p = f;
return false;
}
else if( key == t->data ){
*p = t;
return true;
}
else if( key < t->data ){
return searchAVL( t->left,key,t,p);
}
else{
return searchAVL( t->right,key,t,p);
}
}
/************************************************
* 前序遍历
************************************************/
void PreOrderTraverse(TreeNode *t){
//printf("in PreOrderTraverse\n");
if( NULL == t ) return;
printf("%2d ",t->data);
PreOrderTraverse(t->left);
PreOrderTraverse(t->right);
}
/************************************************
* 前序遍历平衡因子
************************************************/
void PreOrderTraverse_bf(TreeNode *t){
//printf("in PreOrderTraverse\n");
if( NULL == t ) return;
printf("%2d ",t->bf);
PreOrderTraverse_bf(t->left);
PreOrderTraverse_bf(t->right);
}
/************************************************
* 中序遍历
************************************************/
void InOrderTraverse(TreeNode *t){
if( NULL == t ) return;
InOrderTraverse(t->left);
printf("%2d ",t->data);
InOrderTraverse(t->right);
}
/************************************************
* 后序遍历
************************************************/
void PostOrderTraverse(TreeNode *t){
if( NULL == t ) return;
PostOrderTraverse(t->left);
PostOrderTraverse(t->right);
printf("%2d ",t->data);
}
测试代码和用例
int main(){
    int i = , key = ;
    int arr[] = {, , , , , , , , , , -, };
    TreeNode *t = NULL, *p = NULL;
    bool taller = false;
    bool lower = false;
    for( ; i < sizeof(arr)/sizeof(int); i++ ){
        insertAVL(&t,arr[i],&taller);
    }
    printf("value %d is%s in the tree\n",key,( searchAVL(t,key,NULL,&p) ) ? "" : " not");
    key = ;
    printf("value %d is%s in the tree\n",key,( searchAVL(t,key,NULL,&p) ) ? "" : " not");
    printf("\nPreOrderTraverse:\n");
    PreOrderTraverse(t);
    printf("\nPreOrderTraverse_bf:\n");
    PreOrderTraverse_bf(t);
    printf("\nInOrderTraverse:\n");
    InOrderTraverse(t);
    printf("\nafter delete 3:\n");
    deleteAVL( &t,,&lower);
    PreOrderTraverse(t);
    printf("\n");
}
value 1 is in the tree
value 15 is not in the tree
PreOrderTraverse:
 4  2  1 -2  3  8  6  5  7  9 10
PreOrderTraverse_bf:
 0  1  1  0  0  0  0  0  0 -1  0
InOrderTraverse:
-2  1  2  3  4  5  6  7  8  9 10
after delete 3:
 4  1 -2  2  8  6  5  7  9 10
C平衡二叉树(AVL)创建和删除的更多相关文章
- 二叉查找树(BST)、平衡二叉树(AVL树)(只有插入说明)
		
二叉查找树(BST).平衡二叉树(AVL树)(只有插入说明) 二叉查找树(BST) 特殊的二叉树,又称为排序二叉树.二叉搜索树.二叉排序树. 二叉查找树实际上是数据域有序的二叉树,即对树上的每个结点, ...
 - Java 树结构实际应用 四(平衡二叉树/AVL树)
		
平衡二叉树(AVL 树) 1 看一个案例(说明二叉排序树可能的问题) 给你一个数列{1,2,3,4,5,6},要求创建一颗二叉排序树(BST), 并分析问题所在.  左边 BST 存在的问题分析: ...
 - 平衡二叉树,AVL树之图解篇
		
学习过了二叉查找树,想必大家有遇到一个问题.例如,将一个数组{1,2,3,4}依次插入树的时候,形成了图1的情况.有建立树与没建立树对于数据的增删查改已经没有了任何帮助,反而增添了维护的成本.而只有建 ...
 - 数据结构与算法--从平衡二叉树(AVL)到红黑树
		
数据结构与算法--从平衡二叉树(AVL)到红黑树 上节学习了二叉查找树.算法的性能取决于树的形状,而树的形状取决于插入键的顺序.在最好的情况下,n个结点的树是完全平衡的,如下图"最好情况&q ...
 - 数据结构【查找】—平衡二叉树AVL
		
/*自己看了半天也没看懂代码,下次再补充说明*/ 解释: 平衡二叉树(Self-Balancing Binary Search Tree 或Height-Balanced Binary Search ...
 - 平衡二叉树AVL - 插入节点后旋转方法分析
		
平衡二叉树 AVL( 发明者为Adel'son-Vel'skii 和 Landis)是一种二叉排序树,其中每一个节点的左子树和右子树的高度差至多等于1. 首先我们知道,当插入一个节点,从此插入点到树根 ...
 - 图解:平衡二叉树,AVL树
		
学习过了二叉查找树,想必大家有遇到一个问题.例如,将一个数组{1,2,3,4}依次插入树的时候,形成了图1的情况.有建立树与没建立树对于数据的增删查改已经没有了任何帮助,反而增添了维护的成本.而只有建 ...
 - 转载:平衡二叉树(AVL Tree)
		
平衡二叉树(AVL Tree) 转载至:https://www.cnblogs.com/jielongAI/p/9565776.html 在学习算法的过程中,二叉平衡树是一定会碰到的,这篇博文尽可能简 ...
 - linux命令 - ln - 创建和删除软、硬链接
		
linux命令 - ln - 创建和删除软.硬链接 在Linux系统中,内核为每一个新创建的文件分配一个Inode(索引结点),每个文件都有一个惟一的inode号.文件属性保存在索引结点里,在访问文件 ...
 
随机推荐
- iOS原生与H5交互
			
一.WKWebView WKWebView 初始化时,有一个参数叫configuration,它是WKWebViewConfiguration类型的参数,而WKWebViewConfiguration ...
 - JSONObject对象
			
1.JSONObject介绍 JSONObject-lib包是一个beans,collections,maps,java arrays和xml和JSON互相转换的包. 方法: 的getString() ...
 - 常见EMC疑问及对策
			
1. 在电磁兼容领域,为什么总是用分贝(dB)的单位描述?10mV是多少dBmV? 答:因为要描述的幅度和频率范围都很宽,在图形上用对数坐标更容易表示,而dB就是用对数表示时的单位,10mV是20dB ...
 - c++第四次作业
			
继承与派生--访问控制 一.知识要点 (一)知识回顾: 基类的成员可以有public.protected.private三种访问属性.基类的自身成员可以对基类中任何一个其他成员进行访问,但是通过基类的 ...
 - 剑指Offer(三十三):丑数
			
剑指Offer(三十三):丑数 搜索微信公众号:'AI-ming3526'或者'计算机视觉这件小事' 获取更多算法.机器学习干货 csdn:https://blog.csdn.net/baidu_31 ...
 - 剑指offer:从尾到头打印链表
			
题目 输入一个链表,按链表值从尾到头的顺序返回一个ArrayList. 解题思路 在不改变链表结构的前提下,因为单向链表本身的结构是从头到尾的,现在用从尾到头遍历打印,可以联想到“先进后出”, 因此我 ...
 - Javascript获取页面的各种坐标汇总
			
说明,本文全部内容都基于各浏览器的标准渲染模式.也就是在HTML文件首部有标签 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transit ...
 - MyEclipse激活代码
			
package TestCase; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStr ...
 - quiver()函数
			
1.quiver函数 一般用于绘制二维矢量场图,函数调用方法如下: 1 quiver(x,y,u,v) 该函数展示了点(x,y)对应的的矢量(u,v).其中,x的长度要求等于u.v的列数,y的长度要求 ...
 - Hbase表结构
			
1.Hbase表结构:可以看成map,里面有行键,行键是按照字母顺序排序.行键下面是列族,每个列族可以有不同数量的列甚至是没有列.每个列里面包含着不同时间版本的列的值. 行键:是按照字母的顺序排序的, ...