二叉平衡樹的插入和刪除操作


1.      二叉平衡樹

二叉排序樹查找、插入和刪除操作的時間復雜度和樹的深度n有關。構建樹時,當先后插入的結點按關鍵字有序時,二叉排序樹退化為單枝樹,平均查找長度為(n+1)/2,查找效率比較低。提高查找效率,關鍵在於最大限度地降低樹的深度n。因此需要在構建二叉排序樹的過程中進行平衡化處理,使之成為二叉平衡樹。

二叉平衡樹,又稱AVL樹。它或者是一棵空樹,或者是具有下列性質的樹:

1)      具備二叉排序樹的所有性質;

2)      左子樹和右子樹深度差的絕對值不超過1

3)      左子樹和右子樹都是二叉平衡樹。

二叉平衡樹結點的平衡因子定義為左子樹與右子樹的深度之差。二叉平衡樹結點的平衡因子只可能取-101三個值。含有n個結點的二叉平衡樹的深度與logn同數量級,平均查找長度也和logn同數量級。

二叉平衡樹采用二叉鏈表的結構進行存儲。結構體中增加結點的高度,用以計算結點的平衡因子。結點高度定義:空結點的高度為0;非空結點的高度為以該結點為根結點的樹的高度。

二叉鏈表:

/* 
 * 二叉樹的二叉鏈表存儲結構。
 * 額外添加樹的高度,以判斷結點的平衡度。
 */
typedef int TElemType;
typedef struct BiNode
{
	TElemType data;
	struct BiNode *lchild;
	struct BiNode *rchild;
	int height;
}BiNode, *BiTree;

結點高度:

/*
 * 當T=NULL ,即樹為空樹時,無法通過T->height獲取樹的高度0,所以要額外編寫該函數。
 */
int GetHeight(BiTree T)
{
	if (T)
		return T->height;
	return 0;
}

2.      處理失衡的四種旋轉方式

如何在插入結點的時候進行“平衡化”處理?當在樹中插入一個結點時,檢查樹是否因插入操作而失衡,若失衡,則找出其中的最小不平衡二叉樹,對最小不平衡二叉樹進行調整,以達到新的平衡。最小不平衡二叉樹定義為以離插入結點最近,且平衡因子絕對值大於1的結點作為根結點的樹。

對最小不平衡樹進行調整的操作是旋轉,共有4種旋轉方式LL型,LR型,RL型,RR型,分別介紹如下:

1)        LL(單次右旋)

當根結點左子樹的左子樹中的節點導致根結點的平衡因子為2時,采用LL型旋轉進行調整。圖示為兩種需進行單次右旋的不平衡樹。

clip_image002clip_image004

LL型旋轉即單次右旋,是將根結點的左孩子作為新的根結點,根結點左孩子的右子樹作為老根結點的左子樹。圖示如下:

clip_image006

注意:旋轉之后,整個樹中只有結點k1,k2的高度發生變化,而x,y,z三棵子樹中所有結點的高度均未發生變化。

代碼:

/*
 * 當T的左子樹的左子樹上的節點使得T的平衡度為2時,以T為中心進行右旋。
 */
bool LLRotate(BiTree *T)
{
	BiTree lc;
	lc = (*T)->lchild;
	(*T)->lchild = lc->rchild;
	lc->rchild = (*T);

	//注意要更新結點的高度。整個樹中只有*T的左子樹和lc的右子樹發生了變化,所以只需更改這兩棵樹的高度。
	(*T)->height = max(GetHeight((*T)->lchild), GetHeight((*T)->rchild)) + 1;
	lc->height = max(GetHeight(lc->lchild), GetHeight(lc->rchild)) + 1;

	*T = lc;
	return true;
}

2)        RR(單次左旋)

當根結點右子樹的右子樹中的節點導致根結點的平衡因子為-2時,采用RR型旋轉進行調整。圖示為兩種需進行單次左旋的不平衡樹。RR型旋轉與LL型旋轉相對稱。

clip_image008

RR型旋轉即單次左旋,是將根結點的右孩子作為新的根結點,根結點右孩子的左子樹作為老根結點的右子樹。圖示如下:

clip_image012

注意:旋轉之后,整個樹中只有結點k1,k2的高度發生變化,而x,y,z三棵子樹中所有結點的高度均未發生變化。

代碼:

/*
 * 當T的右子樹的右子樹上的節點使得T的平衡度為-2時,以T為中心進行左旋。
 */
bool RRRotate(BiTree *T)
{
	BiTree rc;
	rc = (*T)->rchild;
	(*T)->rchild = rc->lchild;
	rc->lchild = (*T);

	//注意要更新結點的高度。整個樹中只有*T的左子樹和lc的右子樹發生了變化,所以只需更改這兩棵樹的高度。
	(*T)->height = max(GetHeight((*T)->lchild), GetHeight((*T)->rchild)) + 1;
	rc->height = max(GetHeight(rc->lchild), GetHeight(rc->rchild)) + 1;

	*T = rc;
	return true;
}

3)        LR(先單次左旋,再單次右旋)

當根結點左子樹的右子樹中的節點導致根結點的平衡因子為2時,采用LR型旋轉進行調整。圖示為兩種需進行LR型旋轉的不平衡樹。

clip_image014clip_image016

LR型旋轉是先以根結點的左孩子為中心進行單次左旋,再以根結點為中心進行單次右旋。圖示如下:

代碼:

/*
 * 當T的左子樹的右子樹上的節點使得T的平衡度為2時,
 * 先以T的左子樹為中心進行左旋,再以T為中心進行右旋。
 */
bool LRRotate(BiTree *T)
{
	RRRotate(&((*T)->lchild));
	LLRotate(T);
	return true;
}

4)        RL型(先單次右旋,再單次左旋)

當根結點右子樹的左子樹中的節點導致根結點的平衡因子為-2時,采用RL型旋轉進行調整。圖示為兩種需進行RL型旋轉的不平衡樹。RL型旋轉與LR型旋轉相對應。

clip_image020clip_image022

RL型旋轉是先以根結點的右孩子為中心進行單次右旋,再以根結點為中心進行單次左旋。圖示如下:

代碼:

/*
 * 當T的右子樹的左子樹上的節點使得T的平衡度為-2時,
 * 先以T的右子樹為中心進行右旋,再以T為中心進行左旋。
 */
bool RLRotate(BiTree *T)
{
	LLRotate(&((*T)->rchild));
	RRRotate(T);
	return true;
}

3.      插入操作

插入操作的代碼如下。

/*
 * 插入操作。
 * 如果以*T為根結點的二叉平衡樹中已有結點key,插入失敗,函數返回FALSE;
 * 否則將結點key插入到樹中,插入結點后的樹仍然為二叉平衡樹,函數返回TRUE。
 */
bool AVLInsert(BiTree *T, TElemType key)
{
	BiTree t;

	//如果當前查找的根結點為空樹,表明查無此結點,故插入結點。
	if (!*T)
	{
		t = (BiTree)malloc(sizeof(BiNode));
		t->data = key;
		t->height = 1;
		t->lchild = NULL;
		t->rchild = NULL;
		*T = t;
		return true;
	}
	//已有此結點,不再插入。
	else if (key == (*T)->data)
	{
		return false;
	}
	//在左子樹中遞歸插入。
	else if (key < (*T)->data)
	{
		if (!AVLInsert(&((*T)->lchild), key))
			return false;
		else
		{
			//插入成功,修改樹的高度。
			(*T)->height = max(GetHeight((*T)->lchild), GetHeight((*T)->rchild)) + 1;

			//已在*T的左子樹插入結點key,判斷是否需要進行旋轉以保持二叉平衡樹的特性。
			if (2 == GetHeight((*T)->lchild) - GetHeight((*T)->rchild))
			{
				//在左子樹的左子樹中插入結點。
				if (GetHeight((*T)->lchild->lchild) > GetHeight((*T)->lchild->rchild))
				{
					LLRotate(T);
				}
				//在左子樹的右子樹中插入結點。
				else
				{
					LRRotate(T);
				}
			}
			return true;
		}
	}
	//在右子樹中遞歸插入。
	else // (key > (*T)->data)
	{
		if (!AVLInsert(&(*T)->rchild, key))
			return false;
		else
		{
			//插入成功,修改樹的高度。
			(*T)->height = max(GetHeight((*T)->lchild), GetHeight((*T)->rchild)) + 1;

			//已在*T的右子樹插入結點key,判斷是否需要進行旋轉以保持二叉平衡樹的特性。
			if (-2 == GetHeight((*T)->lchild) - GetHeight((*T)->rchild))
			{
				//在右子樹的左子樹中插入結點。
				if (GetHeight((*T)->rchild->lchild) > GetHeight((*T)->rchild->rchild))
				{
					RLRotate(T);
				}
				//在右子樹的右子樹中插入結點。
				else
				{
					RRRotate(T);
				}
			}
			return true;
		}
	}
}

以下圖為例進行兩個關鍵點的說明:進行旋轉的樹為最小不平衡二叉樹;插入結點之后父結點高度的遞歸修正。

假如要在圖一二叉平衡樹中插入結點1

函數調用步驟:

1

調用函數AVLInsert(&9,1)(為表述方便,以&9代表指向結點9的指針)

2

由於1<9,繼續調用AVLInsert(&7,1)

3

由於1<7,繼續調用AVLInsert(&3,1)

4

由於1<3,繼續調用AVLInsert(&2,1)

5

由於2<1,繼續調用AVLInsert(NULL,1),此時由於*T為空樹,增加結點1,並把結點1的高度設置為1,左右孩子分別為空樹,如圖二所示。函數返回TRUE

6

AVLInsert(NULL,1)函數返回TRUE,並返回至AVLInsert(&2,1),因插入成功,所以更新結點2的高度為max(1,0)+1=2,結點2的平衡因子為1,不進行旋轉操作,函數返回TRUE

7

AVLInsert(&2,1)函數返回TRUE,並返回至AVLInsert(&3,1),因插入成功,所以更新結點3的高度為max(2,1)+1=3,結點3的平衡因子為1,不進行旋轉操作,函數返回TRUE

8

AVLInsert(&3,1)函數返回TRUE,並返回至AVLInsert(&7,1),因插入成功,所以更新結點7的高度為max(3,1)+1=4,結點7的平衡因子為2,進行旋轉操作,旋轉之后,更新結點3的高度為2,結點7的高度為2,如圖三所示。函數返回TRUE

9

AVLInsert(&7,1)函數返回TRUE,並返回至AVLInsert(&9,1),因插入成功,所以更新結點9的高度為max(2,1)+1=4,結點9的平衡因子為1,不進行旋轉操作,函數返回TRUE,插入過程結束。

插入的結點一定為葉子結點,插入結點之后依次進行遞歸調用的返回操作,在返回之后,修正父結點的高度(2->3->7->9),之后判斷父結點的平衡因子,當平衡因子超范圍(結點7)時,以該結點為根結點的樹為最小不平衡二叉樹,此時進行旋轉操作。當AVLInsert(&7,1)函數返回之后,以結點7的父結點9為根結點的樹將不再需要進行旋轉操作。因此每次通過函數AVLInsert()插入一個結點時,旋轉操作只在最小不平衡二叉樹中進行一次。已插入結點的父結點的高度是在遞歸過程中依次進行修正的。

4.      刪除操作

刪除操作的代碼如下。

/*
 * 刪除操作。
 * 如果以*T為根結點的樹中存在結點key,將結點刪除,函數返回TRUE,
 * 否則刪除失敗,函數返回FALSE。
 */
bool AVLDelete(BiTree *T, TElemType key)
{
	BiTree pre, post;

	//沒有找到該結點。
	if (!*T)
		return false;
	//找到結點,將它刪除。
	else if (key == (*T)->data)
	{
		//待刪除節點為葉子結點。
		if (!(*T)->lchild && !(*T)->rchild)
			*T = NULL;
		//待刪除結點只有右孩子。
		else if (!(*T)->lchild)
			*T = (*T)->rchild;
		//待刪除結點只有左孩子。
		else if (!(*T)->rchild)
			*T = (*T)->lchild;
		//待刪除結點既有左孩子,又有右孩子。
		else
		{
			//當待刪除結點*T左子樹的高度大於右子樹的高度時,用*T的前驅結點pre代替*T,
			//再將結點pre從樹中刪除。這樣可以保證刪除結點后的樹仍為二叉平衡樹。
			if (GetHeight((*T)->lchild) > GetHeight((*T)->rchild))
			{
				//尋找前驅結點pre。
				pre = (*T)->lchild;
				while (pre->rchild)
				{
					pre = pre->rchild;
				}
				//用pre替換*T。
				(*T)->data = pre->data;
				
				//刪除節點pre。
				//雖然能夠確定pre所屬最小子樹的根結點為&pre,
				//但是不采用AVLDelete(&pre,pre->data)刪除pre,目的是方便遞歸更改節點的高度。
				AVLDelete(&((*T)->lchild), pre->data);
			}
			//當待刪除結點*T左子樹的高度小於或者等於右子樹的高度時,用*T的后繼結點post代替*T,
			//再將結點post從樹中刪除。這樣可以保證刪除結點后的樹仍為二叉平衡樹。
			else
			{
				//尋找后繼節點post。
				post = (*T)->rchild;
				while (post->lchild)
					post = post->lchild;
				//用post替換*T。
				(*T)->data = post->data;

				//刪除節點post。
				//雖然能夠確定post所屬最小子樹的根結點為&post,
				//但是不采用AVLDelete(&post,post->data)刪除post,目的是方便遞歸更改節點的高度。
				AVLDelete(&((*T)->rchild), post->data);
			}
		}
		return true;
	}
	//在左子樹中遞歸刪除。
	else if (key < (*T)->data)
	{
		if (!AVLDelete(&((*T)->lchild), key))
			return false;
		else
		{
			//刪除成功,修改樹的高度。
			(*T)->height = max(GetHeight((*T)->lchild), GetHeight((*T)->rchild)) + 1;
			//已在*T的左子樹刪除結點key,判斷是否需要進行旋轉以保持二叉平衡樹的特性。
			if (-2 == GetHeight((*T)->lchild) - GetHeight((*T)->rchild))
			{
				if (GetHeight((*T)->rchild->lchild) > GetHeight((*T)->rchild->rchild))
				{
					RLRotate(T);
				}
				else
				{
					RRRotate(T);
				}
			}
			return true;
		}
	}
	//在右子樹中遞歸刪除。
	else
	{
		if (!AVLDelete(&((*T)->rchild), key))
			return false;
		else
		{
			//刪除成功,修改樹的高度。
			(*T)->height = max(GetHeight((*T)->lchild), GetHeight((*T)->rchild)) + 1;
			//已在*T的右子樹刪除結點key,判斷是否需要進行旋轉以保持二叉平衡樹的特性。
			if (2 == GetHeight((*T)->lchild) - GetHeight((*T)->rchild))
			{
				if (GetHeight((*T)->lchild->lchild) > GetHeight((*T)->lchild->rchild))
				{
					LLRotate(T);
				}
				else
				{
					LRRotate(T);
				}
			}
			return true;
		}
	}
}

關鍵點:

1. 當待刪除結點*T既有左子樹又有右子樹且左子樹高度大於右子樹高度時,用結點*T的前驅結點pre替換*T,之后再刪除前驅結點pre;當右子樹高度大於左子樹高度時,用結點*T的后繼節點post替換結點*T,之后再刪除后繼結點post。這樣可以保證在刪除操作之后,樹在不進行旋轉操作的情況下仍為二叉平衡樹。

2.   在刪除前驅結點pre和后繼結點post時,使用AVLDelete(&((*T)->lchild), pre->data)AVLDelete(&((*T)->rchild), post->data)。這樣可以保證被刪除結點prepost的父節點直至根結點的結點高度都會被遞歸修正一次。結點高度的遞歸修正同插入操作。

5.      時間復雜度

在平衡樹上進行查找的過程和排序樹相同,因此在查找過程中和給定值進行比較的關鍵字個數不超過樹的深度。假設F(N)表示N層平衡二叉樹的最少結點個數,則F[1]=1F[2]=2F(N)=F(N-2)+F(N-1)+1。在平衡樹上進行查找的時間復雜度為O(logn)

6.      完整源代碼

#ifndef AVL_H
#define AVL_H

#include <stdio.h>
#include <stdlib.h>    //內含max(a,b)宏,故不需自己編寫。
#include <stdbool.h>

/* 
 * 二叉樹的二叉鏈表存儲結構。
 * 額外添加樹的高度,以判斷結點的平衡度。
 */
typedef int TElemType;
typedef struct BiNode
{
    TElemType data;
    struct BiNode *lchild;
    struct BiNode *rchild;
    int height;
}BiNode, *BiTree;

//函數聲明
bool AVLInsert(BiTree *T, TElemType key);
bool AVLDelete(BiTree *T, TElemType key);
bool LLRotate(BiTree *T);
bool LRRotate(BiTree *T);
bool RLRotate(BiTree *T);
bool RRRotate(BiTree *T);
int GetHeight(BiTree T);

#endif
AVL.h
/*
 * 實現二叉平衡樹的插入、刪除操作
 */

#include "AVL.h"

/*
 * 插入操作。
 * 如果以*T為根結點的二叉平衡樹中已有結點key,插入失敗,函數返回FALSE;
 * 否則將結點key插入到樹中,插入結點后的樹仍然為二叉平衡樹,函數返回TRUE。
 */
bool AVLInsert(BiTree *T, TElemType key)
{
    BiTree t;

    //如果當前查找的根結點為空樹,表明查無此結點,故插入結點。
    if (!*T)
    {
        t = (BiTree)malloc(sizeof(BiNode));
        t->data = key;
        t->height = 1;
        t->lchild = NULL;
        t->rchild = NULL;
        *T = t;
        return true;
    }
    //已有此結點,不再插入。
    else if (key == (*T)->data)
    {
        return false;
    }
    //在左子樹中遞歸插入。
    else if (key < (*T)->data)
    {
        if (!AVLInsert(&((*T)->lchild), key))
            return false;
        else
        {
            //插入成功,修改樹的高度。
            (*T)->height = max(GetHeight((*T)->lchild), GetHeight((*T)->rchild)) + 1;

            //已在*T的左子樹插入結點key,判斷是否需要進行旋轉以保持二叉平衡樹的特性。
            if (2 == GetHeight((*T)->lchild) - GetHeight((*T)->rchild))
            {
                //在左子樹的左子樹中插入結點。
                if (GetHeight((*T)->lchild->lchild) > GetHeight((*T)->lchild->rchild))
                {
                    LLRotate(T);
                }
                //在左子樹的右子樹中插入結點。
                else
                {
                    LRRotate(T);
                }
            }
            return true;
        }
    }
    //在右子樹中遞歸插入。
    else // (key > (*T)->data)
    {
        if (!AVLInsert(&(*T)->rchild, key))
            return false;
        else
        {
            //插入成功,修改樹的高度。
            (*T)->height = max(GetHeight((*T)->lchild), GetHeight((*T)->rchild)) + 1;

            //已在*T的右子樹插入結點key,判斷是否需要進行旋轉以保持二叉平衡樹的特性。
            if (-2 == GetHeight((*T)->lchild) - GetHeight((*T)->rchild))
            {
                //在右子樹的左子樹中插入結點。
                if (GetHeight((*T)->rchild->lchild) > GetHeight((*T)->rchild->rchild))
                {
                    RLRotate(T);
                }
                //在右子樹的右子樹中插入結點。
                else
                {
                    RRRotate(T);
                }
            }
            return true;
        }
    }
}

/*
 * 刪除操作。
 * 如果以*T為根結點的樹中存在結點key,將結點刪除,函數返回TRUE,
 * 否則刪除失敗,函數返回FALSE。
 */
bool AVLDelete(BiTree *T, TElemType key)
{
    BiTree pre, post;

    //沒有找到該結點。
    if (!*T)
        return false;
    //找到結點,將它刪除。
    else if (key == (*T)->data)
    {
        //待刪除節點為葉子結點。
        if (!(*T)->lchild && !(*T)->rchild)
            *T = NULL;
        //待刪除結點只有右孩子。
        else if (!(*T)->lchild)
            *T = (*T)->rchild;
        //待刪除結點只有左孩子。
        else if (!(*T)->rchild)
            *T = (*T)->lchild;
        //待刪除結點既有左孩子,又有右孩子。
        else
        {
            //當待刪除結點*T左子樹的高度大於右子樹的高度時,用*T的前驅結點pre代替*T,
            //再將結點pre從樹中刪除。這樣可以保證刪除結點后的樹仍為二叉平衡樹。
            if (GetHeight((*T)->lchild) > GetHeight((*T)->rchild))
            {
                //尋找前驅結點pre。
                pre = (*T)->lchild;
                while (pre->rchild)
                {
                    pre = pre->rchild;
                }
                //用pre替換*T。
                (*T)->data = pre->data;
                
                //刪除節點pre。
                //雖然能夠確定pre所屬最小子樹的根結點為&pre,
                //但是不采用AVLDelete(&pre,pre->data)刪除pre,目的是方便遞歸更改節點的高度。
                AVLDelete(&((*T)->lchild), pre->data);
            }
            //當待刪除結點*T左子樹的高度小於或者等於右子樹的高度時,用*T的后繼結點post代替*T,
            //再將結點post從樹中刪除。這樣可以保證刪除結點后的樹仍為二叉平衡樹。
            else
            {
                //尋找后繼節點post。
                post = (*T)->rchild;
                while (post->lchild)
                    post = post->lchild;
                //用post替換*T。
                (*T)->data = post->data;

                //刪除節點post。
                //雖然能夠確定post所屬最小子樹的根結點為&post,
                //但是不采用AVLDelete(&post,post->data)刪除post,目的是方便遞歸更改節點的高度。
                AVLDelete(&((*T)->rchild), post->data);
            }
        }
        return true;
    }
    //在左子樹中遞歸刪除。
    else if (key < (*T)->data)
    {
        if (!AVLDelete(&((*T)->lchild), key))
            return false;
        else
        {
            //刪除成功,修改樹的高度。
            (*T)->height = max(GetHeight((*T)->lchild), GetHeight((*T)->rchild)) + 1;
            //已在*T的左子樹刪除結點key,判斷是否需要進行旋轉以保持二叉平衡樹的特性。
            if (-2 == GetHeight((*T)->lchild) - GetHeight((*T)->rchild))
            {
                if (GetHeight((*T)->lchild->lchild) > GetHeight((*T)->lchild->rchild))
                {
                    LLRotate(T);
                }
                else
                {
                    LRRotate(T);
                }
            }
            return true;
        }
    }
    //在右子樹中遞歸刪除。
    else
    {
        if (!AVLDelete(&((*T)->rchild), key))
            return false;
        else
        {
            //刪除成功,修改樹的高度。
            (*T)->height = max(GetHeight((*T)->lchild), GetHeight((*T)->rchild)) + 1;
            //已在*T的右子樹刪除結點key,判斷是否需要進行旋轉以保持二叉平衡樹的特性。
            if (2 == GetHeight((*T)->lchild) - GetHeight((*T)->rchild))
            {
                if (GetHeight((*T)->rchild->lchild) > GetHeight((*T)->rchild->rchild))
                {
                    RLRotate(T);
                }
                else
                {
                    RRRotate(T);
                }
            }
            return true;
        }
    }
}

/*
 * 當T的左子樹的左子樹上的節點使得T的平衡度為2時,以T為中心進行右旋。
 */
bool LLRotate(BiTree *T)
{
    BiTree lc;
    lc = (*T)->lchild;
    (*T)->lchild = lc->rchild;
    lc->rchild = (*T);

    //注意要更新結點的高度。整個樹中只有*T的左子樹和lc的右子樹發生了變化,所以只需更改這兩棵樹的高度。
    (*T)->height = max(GetHeight((*T)->lchild), GetHeight((*T)->rchild)) + 1;
    lc->height = max(GetHeight(lc->lchild), GetHeight(lc->rchild)) + 1;

    *T = lc;
    return true;
}

/*
 * 當T的左子樹的右子樹上的節點使得T的平衡度為2時,
 * 先以T的左子樹為中心進行左旋,再以T為中心進行右旋。
 */
bool LRRotate(BiTree *T)
{
    RRRotate(&((*T)->lchild));
    LLRotate(T);
    return true;
}

/*
 * 當T的右子樹的左子樹上的節點使得T的平衡度為-2時,
 * 先以T的右子樹為中心進行右旋,再以T為中心進行左旋。
 */
bool RLRotate(BiTree *T)
{
    LLRotate(&((*T)->rchild));
    RRRotate(T);
    return true;
}

/*
 * 當T的右子樹的右子樹上的節點使得T的平衡度為-2時,以T為中心進行左旋。
 */
bool RRRotate(BiTree *T)
{
    BiTree rc;
    rc = (*T)->rchild;
    (*T)->rchild = rc->lchild;
    rc->lchild = (*T);

    //注意要更新結點的高度。整個樹中只有*T的左子樹和lc的右子樹發生了變化,所以只需更改這兩棵樹的高度。
    (*T)->height = max(GetHeight((*T)->lchild), GetHeight((*T)->rchild)) + 1;
    rc->height = max(GetHeight(rc->lchild), GetHeight(rc->rchild)) + 1;

    *T = rc;
    return true;
}

/*
 * 當T=NULL ,即樹為空樹時,無法通過T->height獲取樹的高度0,所以要額外編寫該函數。
 */
int GetHeight(BiTree T)
{
    if (T)
        return T->height;
    return 0;
}
AVL.c
/*
 * 二叉平衡樹的插入和刪除操作
 */
#define _CRT_SECURE_NO_WARNINGS
#include "AVL.h"

#define TOTAL 10
bool InOrderTraverse(BiTree T);
bool print(BiTree *T);

int main()
{
    BiTree T = NULL;    //注意一定要將T初始化為空樹。
    int i, num;

    //插入操作。
    printf("輸入Total個數以構建二叉平衡樹:\n");
    for (i = 0; i < TOTAL; i++)
    {
        scanf("%d", &num);
        AVLInsert(&T, num);
    }

    //檢驗插入操作。
    printf("樹結構為:\n");
    print(&T);
    printf("中序遍歷該樹,檢驗是否為由小到大:\n");
    InOrderTraverse(T);
    putchar('\n');
    printf("樹的深度為:%d\n", T->height);
    

    //刪除操作。
    printf("輸入待刪除的數:");
    scanf("%d", &num);
    AVLDelete(&T, num);

    //檢驗刪除操作。
    printf("刪除后,樹結構為:\n");
    print(&T);
    printf("中序遍歷該樹,檢驗是否為由小到大:\n");
    InOrderTraverse(T);
    putchar('\n');
    printf("樹的深度為:%d\n", T->height);
    return 0;
}

/*
 * 中序遍歷輸出樹結構。
 */
bool InOrderTraverse(BiTree T)
{
    if (T)
    {
        InOrderTraverse(T->lchild);
        printf("%-3d ", T->data);
        InOrderTraverse(T->rchild);
    }
    return true;
}

/*
 * 輸出樹結構,且標明父子關系。
 */
bool print(BiTree *T)
{
    
    if (!*T)
        return false;
    else
    {
        //如果樹有左孩子或者右孩子,則輸出它的左孩子和右孩子。
        //例:5有左孩子3和右孩子6,輸出形式為:5(3,6)
        //5只有左孩子3,輸出形式為:5(3, )
        //5只有右孩子6,輸出形式為:5( ,6)
        if ((*T)->lchild || (*T)->rchild)
        {
            printf("%d(", (*T)->data);
            if ((*T)->lchild)
            {
                printf("%-3d,", (*T)->lchild->data);
            }
            else
            {
                printf("   ,");
            }
            if ((*T)->rchild)
            {
                printf("%-3d)\n", (*T)->rchild->data);
            }
            else
            {
                printf("   )\n");
            }
        }

        //遞歸輸出左孩子和右孩子。
        print(&((*T)->lchild));
        print(&((*T)->rchild));
        return true;
    }
}
main.c

7.      測試結果

 無標題_2345看圖王

無標題2_2345看圖王

無標題3_2345看圖王

 

 參考:

【查找結構3】平衡查找二叉樹[AVL]

AVL樹(一)之圖文解析和C語言實現

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM