看《数据结构与算法分析》第四章时介绍到了一个AVL树的东西
对于节点删除,书上只是提了一下惰性删除,也没有给出例程,这里就试着实现非惰性删除
实现的方法同二叉查找树,找一个左子树的最大节点进行替换,递归删除,并做及时的AVL树维护
对应后面的习题
4.16 指出将2,1,4,5,9,3,6,7插入到初始空的AVL树后的结果。
4.18 写出实现AVL单旋转和双旋转的其余的过程
4.20 如何能够在AVL树中实现(非惰性)删除
4.22 写出执行双旋转的函数,其效率要超过执行两个单旋转
1.AVLTree.h
先给出头文件
#ifndef _SEARCHTREE_H_
#define _SEARCHTREE_H_
#define ElementType int
//#define DELMETHOD_VIRTUAL //假性删除
#define DELMETHOD_REAL //非假性删除
struct TreeNode;
typedef struct TreeNode * pTreeNode;
struct TreeNode
{
ElementType data;
pTreeNode child_l;
pTreeNode child_r;
int height;
#ifdef DELMETHOD_VIRTUAL
int del; //del用于记录节点是否被删除了
#endif
};
pTreeNode AVLTree_insert (pTreeNode T, ElementType d);
pTreeNode AVLTree_delete (pTreeNode T, ElementType d);
pTreeNode AVLTree_find (pTreeNode T, ElementType d);
void AVLTree_print (pTreeNode T);
#endif
2.AVLTree.c
AVL树实现里,最麻烦是每次在树有变化时,都需要保证满足树的条件
所以要不断检查,旋转
插入的和书上的差不多,不过双旋转的情况下,会比调用两次单旋转更快一些
#include
#include
#include
#include "AVLTree.h"
#define getheight(T) ( ( T )? T->height : 0 )
#define MAX(A,B) ( ( A > B )? A : B )
#define CompareChild(T) ( getheight(T->child_l) > getheight(T->child_r) ? 1 : 0)
static int changeheight (pTreeNode T)
{
if ( T == NULL )
return 0;
T->height = MAX ( changeheight (T->child_l), changeheight (T->child_r) ) + 1;
return T->height;
}
static pTreeNode rotate (pTreeNode A, pTreeNode B)
{
pTreeNode C;
if ( CompareChild(A) && CompareChild(B) ) //left left
{
A->child_l = B->child_r;
B->child_r = A;
changeheight (B);
return B;
}
if ( !CompareChild(A) && !CompareChild(B) ) //right right
{
A->child_r = B->child_l;
B->child_l = A;
changeheight (B);
return B;
}
if ( CompareChild(A) && !CompareChild(B) ) //left right
{
C = B->child_r;
B->child_r = C->child_l;
A->child_l = C->child_r;
C->child_r = A;
C->child_l = B;
changeheight (C);
return C;
}
if ( !CompareChild(A) && CompareChild(B) ) //right left
{
C = B->child_l;
B->child_l = C->child_r;
A->child_r = C->child_l;
C->child_l = A;
C->child_r = B;
changeheight (C);
return C;
}
return 0; //means wrong
}
pTreeNode AVLTree_insert (pTreeNode T, ElementType d)
{
if ( T == NULL )
{
T = malloc ( sizeof (struct TreeNode) );
T->data = d;
T->child_l = NULL;
T->child_r = NULL;
T->height = 1;
#ifdef DELMETHOD_VIRTUAL
T->del = 0;
#endif
return T;
}
else
{
if ( T->data == d )
{
#ifdef DELMETHOD_VIRTUAL
T->del = 0;
#endif
return T;
}
if ( T->data > d )
T->child_l = AVLTree_insert ( T->child_l, d );
else
T->child_r = AVLTree_insert ( T->child_r, d );
//当左右两节点差值大于1就要旋转了
if ( getheight( T->child_r ) - getheight ( T->child_l ) > 1 )
T = rotate ( T, T->child_r );
if ( getheight( T->child_l ) - getheight ( T->child_r ) > 1 )
T = rotate ( T, T->child_l );
T->height = MAX ( getheight ( T->child_l ), getheight ( T->child_r ) ) + 1;
return T;
}
}
删除也是挺麻烦的,主要是会影响本来的结构
所以在替换(删除)后,要进行一次左节点与右节点的高度比较,必要时进行一次旋转
这里同时实现了惰性删除和非惰性删除,通过头文件里的define决定
#ifdef DELMETHOD_REAL
static pTreeNode del_changeheight (pTreeNode T)
{
int l = 0;
int r = 0;
l = getheight(T->child_l);
r = getheight(T->child_r);
if ( l - r > 1 )
T = rotate ( T, T->child_l );
else
{
if ( r - l > 1 )
T = rotate ( T, T->child_r );
else
T->height = MAX ( l, r ) + 1;
}
return T;
}
#endif
pTreeNode AVLTree_delete (pTreeNode T, ElementType d)
{
/*****************************************************************/
#ifdef DELMETHOD_REAL //非假性删除方法
pTreeNode temp, temp_p;
//find the node to delete
if ( T->data > d)
{
T->child_l = AVLTree_delete (T->child_l, d);
T = del_changeheight (T); //关键每次递归回来都需要检查一次高度是否改变,如果改变了要即使旋转
}
if ( T->data < d)
{
T->child_r = AVLTree_delete (T->child_r, d);
T = del_changeheight (T);
}
if ( T->data == d)
{
if ( T->child_l && T->child_r ) //使用左子树的最大元素作替换
{
temp_p = T;
temp = T->child_l;
while ( temp->child_r ) //find the node to subs
{
temp_p = temp;
temp = temp->child_r;
}
T->data = temp->data;
T->child_l = AVLTree_delete ( T->child_l, temp->data ); //替换完后再递归删除这个节点
T = del_changeheight (T);
}
else //当要删除的节点没有两个儿子时就不用那么麻烦了
{
temp = NULL;
if ( T->child_l )
temp = T->child_l;
if ( T->child_r )
temp = T->child_r;
free (T);
if ( temp )
T = del_changeheight (temp);
return temp;
}
}
return T; //这个灰常重要!
#endif
/*****************************************************************/
/*****************************************************************/
#ifdef DELMETHOD_VIRTUAL //假性删除方法,del赋为1
pTreeNode P;
P = AVLTree_find (T, d);
P->del = 1;
return T;
#endif
/*****************************************************************/
}
最后还有一点小功能
pTreeNode AVLTree_find (pTreeNode T, ElementType d)
{
pTreeNode p = T;
while ( p != NULL )
{
if ( p->data == d )
return p;
p = ( p->data > d ) ? p->child_l : p->child_r ;
}
#ifdef DELMETHOD_VIRTUAL
if ( p->del )
return NULL;
#endif
return p;
}
#define AVLTree_print_b(T) AVLTree_print_ (T, 0)
#define AVLTree_print_m(T) AVLTree_print_ (T, 1)
#define AVLTree_print_a(T) AVLTree_print_ (T, 2)
static void AVLTree_print_ (pTreeNode p, int flag)
{
if ( p != NULL )
{
if ( flag == 0 )
printf ("%d ", p->data);
AVLTree_print_ (p->child_l, flag);
if ( flag == 1 )
printf ("%d ", p->data);
AVLTree_print_ (p->child_r, flag);
if ( flag == 2 )
printf ("%d ", p->data);
}
}
void AVLTree_print (pTreeNode T)
{
printf ("\nthe pre-order traversal of the AVLTree is: ");
AVLTree_print_b (T);
printf ("\nthe in-order traversal of the AVLTree is: ");
AVLTree_print_m (T);
printf("\n");
}
3.main.c 实现一下题目的要求
#include
#include
#include
#include "AVLTree.h"
int a[] = {2,1,4,5,9,3,6,7};
int main ()
{
pTreeNode T = NULL;
int i;
for (i = 0; i<8; i++)
T = AVLTree_insert (T, a[i]);
AVLTree_print (T);
T = AVLTree_delete (T, T->data);
AVLTree_print (T);
T = AVLTree_delete (T, 1);
AVLTree_print (T);
T = AVLTree_delete (T, 8);
AVLTree_print (T);
return 0;
}