【算法】数据结构与算法分析学习笔记——第四章AVL树C语言实现

看《数据结构与算法分析》第四章时介绍到了一个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;
}




你可能感兴趣的:(算法)