1. 程式人生 > >【演算法與資料結構】AVL樹

【演算法與資料結構】AVL樹

目錄

概要

AVL樹的介紹

AVL樹的C實現

1. 節點

2. 旋轉

AVL樹的C實現(完整原始碼)

AVL樹的C測試程式


概要

本章介紹AVL樹。和前面介紹"二叉查詢樹"的流程一樣,本章先對AVL樹的理論知識進行簡單介紹,然後給出C語言的實現。本篇實現的二叉查詢樹是C語言版的,後面章節再分別給出C++和Java版本的實現。
建議:若您對"二叉查詢樹"不熟悉,建議先學完"二叉查詢樹"再來學習AVL樹。

AVL樹的介紹

AVL樹是根據它的發明者G.M. Adelson-Velsky和E.M. Landis命名的。
它是最先發明的自平衡二叉查詢樹,也被稱為高度平衡樹。相比於"二叉查詢樹",它的特點是:AVL樹中任何節點的兩個子樹的高度最大差別為1。 (關於樹的高度等基本概念,請參考"

二叉查詢樹(一)之 圖文解析 和 C語言的實現 ")

上面的兩張圖片,左邊的是AVL樹,它的任何節點的兩個子樹的高度差別都<=1;而右邊的不是AVL樹,因為7的兩顆子樹的高度相差為2(以2為根節點的樹的高度是3,而以8為根節點的樹的高度是1)。

AVL樹的查詢、插入和刪除在平均和最壞情況下都是O(logn)。
如果在AVL樹中插入或刪除節點後,使得高度之差大於1。此時,AVL樹的平衡狀態就被破壞,它就不再是一棵二叉樹;為了讓它重新維持在一個平衡狀態,就需要對其進行旋轉處理。學AVL樹,重點的地方也就是它的旋轉演算法;在後文的介紹中,再來對它進行詳細介紹。

AVL樹的C實現

1. 節點

1.1 定義

typedef int Type;

typedef struct AVLTreeNode{
    Type key;                    // 關鍵字(鍵值)
    int height;
    struct AVLTreeNode *left;    // 左孩子
    struct AVLTreeNode *right;    // 右孩子
}Node, *AVLTree;

AVL樹的節點包括的幾個組成物件:
(1) key -- 是關鍵字,是用來對AVL樹的節點進行排序的。
(2) left -- 是左孩子。
(3) right -- 是右孩子。
(4) height -- 是高度。

1.2 節點的建立

/*
 * 建立AVL樹結點。
 *
 * 引數說明:
 *     key 是鍵值。
 *     left 是左孩子。
 *     right 是右孩子。
 */
static Node* avltree_create_node(Type key, Node *left, Node* right)
{
    Node* p;

    if ((p = (Node *)malloc(sizeof(Node))) == NULL)
        return NULL;
    p->key = key;
    p->height = 0;
    p->left = left;
    p->right = right;

    return p;
}

1.3 樹的高度

#define HEIGHT(p)    ( (p==NULL) ? 0 : (((Node *)(p))->height) )

/*
 * 獲取AVL樹的高度
 */
int avltree_height(AVLTree tree)
{
    return HEIGHT(tree);
}

關於高度,有的文章中將"空二叉樹的高度定義為-1",而本文采用維基百科上的定義:樹的高度為最大層次。即空的二叉樹的高度是0,非空樹的高度等於它的最大層次(根的層次為1,根的子節點為第2層,依次類推)。

本文程式碼中定義的結點高度是指該結點的子節點中,較大的子結點高度加1。下面舉幾個例子:

最底層的葉子結點都為1,然後向上尋找根結點,根結點的高度就在其較大的子結點基礎上加1。比如(1)的高度是1,(2)的高度就是2,(4)有2個子結點(2)和(6),他們高度分別為2和1,那麼遵從在較大的高度上加1,那麼(4)的高度就是3,以此類推。

如果某個根結點的左子樹和右子樹的高度相差為2時,就需要做旋轉。具體旋轉操作請看下面介紹。

1.4 比較大小

#define MAX(a, b)    ( (a) > (b) ? (a) : (b) )

2. 旋轉

前面說過,如果在AVL樹中進行插入或刪除節點後,可能導致AVL樹失去平衡。這種失去平衡的可以概括為4種姿態:LL(左左),LR(左右),RR(右右)和RL(右左)。下面給出它們的示意圖:

 

上圖中的4棵樹都是"失去平衡的AVL樹",從左往右的情況依次是:LL、LR、RL、RR。

總的來說,AVL樹失去平衡時的情況一定是LL、LR、RL、RR這4種之一,它們都由各自的定義:

(1) LL:LeftLeft,也稱為"左左"。插入或刪除一個節點後,根節點的左子樹的左子樹還有非空子節點,導致"根的左子樹的高度"比"根的右子樹的高度"大2,導致AVL樹失去了平衡。
     例如,在上面LL情況中,由於"根節點(8)的左子樹(4)的左子樹(2)還有非空子節點",而"根節點(8)的右子樹(12)沒有子節點";導致"根節點(8)的左子樹(4)高度"比"根節點(8)的右子樹(12)"高2。

(2) LR:LeftRight,也稱為"左右"。插入或刪除一個節點後,根節點的左子樹的右子樹還有非空子節點,導致"根的左子樹的高度"比"根的右子樹的高度"大2,導致AVL樹失去了平衡。
     例如,在上面LR情況中,由於"根節點(8)的左子樹(4)的左子樹(6)還有非空子節點",而"根節點(8)的右子樹(12)沒有子節點";導致"根節點(8)的左子樹(4)高度"比"根節點(8)的右子樹(12)"高2。

(3) RL:RightLeft,稱為"右左"。插入或刪除一個節點後,根節點的右子樹的左子樹還有非空子節點,導致"根的右子樹的高度"比"根的左子樹的高度"大2,導致AVL樹失去了平衡。
     例如,在上面RL情況中,由於"根節點(8)的右子樹(12)的左子樹(10)還有非空子節點",而"根節點(8)的左子樹(4)沒有子節點";導致"根節點(8)的右子樹(12)高度"比"根節點(8)的左子樹(4)"高2。

(4) RR:RightRight,稱為"右右"。插入或刪除一個節點後,根節點的右子樹的右子樹還有非空子節點,導致"根的右子樹的高度"比"根的左子樹的高度"大2,導致AVL樹失去了平衡。
     例如,在上面RR情況中,由於"根節點(8)的右子樹(12)的右子樹(14)還有非空子節點",而"根節點(8)的左子樹(4)沒有子節點";導致"根節點(8)的右子樹(12)高度"比"根節點(8)的左子樹(4)"高2。

前面說過,如果在AVL樹中進行插入或刪除節點後,可能導致AVL樹失去平衡。AVL失去平衡之後,可以通過旋轉使其恢復平衡,下面分別介紹"LL(左左),LR(左右),RR(右右)和RL(右左)"這4種情況對應的旋轉方法。

2.1 LL的旋轉

LL失去平衡的情況,可以通過一次旋轉讓AVL樹恢復平衡。如下圖:

 

圖中左邊是旋轉之前的樹,右邊是旋轉之後的樹。從中可以發現,旋轉之後的樹又變成了AVL樹,而且該旋轉只需要一次即可完成。
對於LL旋轉,你可以這樣理解為:LL旋轉是圍繞"失去平衡的AVL根節點"進行的,也就是節點k2;而且由於是LL情況,即左左情況,就用手抓著"左孩子,即k1"使勁搖。將k1變成根節點,k2變成k1的右子樹,"k1的右子樹"變成"k2的左子樹"。

LL的旋轉程式碼

/*
 * LL:左左對應的情況(左單旋轉)。
 *
 * 返回值:旋轉後的根節點
 */
static Node* left_left_rotation(AVLTree k2)
{
    AVLTree k1;

    k1 = k2->left;
    k2->left = k1->right;
    k1->right = k2;

    k2->height = MAX( HEIGHT(k2->left), HEIGHT(k2->right)) + 1;
    k1->height = MAX( HEIGHT(k1->left), k2->height) + 1;

    return k1;
}

 

2.2 RR的旋轉

理解了LL之後,RR就相當容易理解了。RR是與LL對稱的情況!RR恢復平衡的旋轉方法如下:

 

圖中左邊是旋轉之前的樹,右邊是旋轉之後的樹。RR旋轉也只需要一次即可完成。

RR的旋轉程式碼

/*
 * RR:右右對應的情況(右單旋轉)。
 *
 * 返回值:旋轉後的根節點
 */
static Node* right_right_rotation(AVLTree k1)
{
    AVLTree k2;

    k2 = k1->right;
    k1->right = k2->left;
    k2->left = k1;

    k1->height = MAX( HEIGHT(k1->left), HEIGHT(k1->right)) + 1;
    k2->height = MAX( HEIGHT(k2->right), k1->height) + 1;

    return k2;
}

 

2.3 LR的旋轉

LR失去平衡的情況,需要經過兩次旋轉才能讓AVL樹恢復平衡。如下圖:

第一次旋轉是圍繞"k1"進行的"RR旋轉",第二次是圍繞"k3"進行的"LL旋轉"。

LR的旋轉程式碼

/*
 * LR:左右對應的情況(左雙旋轉)。
 *
 * 返回值:旋轉後的根節點
 */
static Node* left_right_rotation(AVLTree k3)
{
    k3->left = right_right_rotation(k3->left);

    return left_left_rotation(k3);
}

 

2.4 RL的旋轉
RL是與LR的對稱情況!RL恢復平衡的旋轉方法如下:

 

第一次旋轉是圍繞"k3"進行的"LL旋轉",第二次是圍繞"k1"進行的"RR旋轉"。


RL的旋轉程式碼

/*
 * RL:右左對應的情況(右雙旋轉)。
 *
 * 返回值:旋轉後的根節點
 */
static Node* right_left_rotation(AVLTree k1)
{
    k1->right = left_left_rotation(k1->right);

    return right_right_rotation(k1);
}


3. 插入
插入節點的程式碼

/* 
 * 將結點插入到AVL樹中,並返回根節點
 *
 * 引數說明:
 *     tree AVL樹的根結點
 *     key 插入的結點的鍵值
 * 返回值:
 *     根節點
 */
Node* avltree_insert(AVLTree tree, Type key)
{
    if (tree == NULL) 
    {
        // 新建節點
        tree = avltree_create_node(key, NULL, NULL);
        if (tree==NULL)
        {
            printf("ERROR: create avltree node failed!\n");
            return NULL;
        }
    }
    else if (key < tree->key) // 應該將key插入到"tree的左子樹"的情況
    {
        tree->left = avltree_insert(tree->left, key);
        // 插入節點後,若AVL樹失去平衡,則進行相應的調節。
        if (HEIGHT(tree->left) - HEIGHT(tree->right) == 2)
        {
            if (key < tree->left->key)
                tree = left_left_rotation(tree);
            else
                tree = left_right_rotation(tree);
        }
    }
    else if (key > tree->key) // 應該將key插入到"tree的右子樹"的情況
    {
        tree->right = avltree_insert(tree->right, key);
        // 插入節點後,若AVL樹失去平衡,則進行相應的調節。
        if (HEIGHT(tree->right) - HEIGHT(tree->left) == 2)
        {
            if (key > tree->right->key)
                tree = right_right_rotation(tree);
            else
                tree = right_left_rotation(tree);
        }
    }
    else //key == tree->key)
    {
        printf("新增失敗:不允許新增相同的節點!\n");
    }

    tree->height = MAX( HEIGHT(tree->left), HEIGHT(tree->right)) + 1;

    return tree;
}

4. 刪除
刪除節點的程式碼

/* 
 * 刪除結點(z),返回根節點
 *
 * 引數說明:
 *     ptree AVL樹的根結點
 *     z 待刪除的結點
 * 返回值:
 *     根節點
 */
static Node* delete_node(AVLTree tree, Node *z)
{
    // 根為空 或者 沒有要刪除的節點,直接返回NULL。
    if (tree==NULL || z==NULL)
        return NULL;

    if (z->key < tree->key)        // 待刪除的節點在"tree的左子樹"中
    {
        tree->left = delete_node(tree->left, z);
        // 刪除節點後,若AVL樹失去平衡,則進行相應的調節。
        if (HEIGHT(tree->right) - HEIGHT(tree->left) == 2)
        {
            Node *r =  tree->right;
            if (HEIGHT(r->left) > HEIGHT(r->right))
                tree = right_left_rotation(tree);
            else
                tree = right_right_rotation(tree);
        }
    }
    else if (z->key > tree->key)// 待刪除的節點在"tree的右子樹"中
    {
        tree->right = delete_node(tree->right, z);
        // 刪除節點後,若AVL樹失去平衡,則進行相應的調節。
        if (HEIGHT(tree->left) - HEIGHT(tree->right) == 2)
        {
            Node *l =  tree->left;
            if (HEIGHT(l->right) > HEIGHT(l->left))
                tree = left_right_rotation(tree);
            else
                tree = left_left_rotation(tree);
        }
    }
    else    // tree是對應要刪除的節點。
    {
        // tree的左右孩子都非空
        if ((tree->left) && (tree->right))
        {
            if (HEIGHT(tree->left) > HEIGHT(tree->right))
            {
                // 如果tree的左子樹比右子樹高;
                // 則(01)找出tree的左子樹中的最大節點
                //   (02)將該最大節點的值賦值給tree。
                //   (03)刪除該最大節點。
                // 這類似於用"tree的左子樹中最大節點"做"tree"的替身;
                // 採用這種方式的好處是:刪除"tree的左子樹中最大節點"之後,AVL樹仍然是平衡的。
                Node *max = avltree_maximum(tree->left);
                tree->key = max->key;
                tree->left = delete_node(tree->left, max);
            }
            else
            {
                // 如果tree的左子樹不比右子樹高(即它們相等,或右子樹比左子樹高1)
                // 則(01)找出tree的右子樹中的最小節點
                //   (02)將該最小節點的值賦值給tree。
                //   (03)刪除該最小節點。
                // 這類似於用"tree的右子樹中最小節點"做"tree"的替身;
                // 採用這種方式的好處是:刪除"tree的右子樹中最小節點"之後,AVL樹仍然是平衡的。
                Node *min = avltree_maximum(tree->right);
                tree->key = min->key;
                tree->right = delete_node(tree->right, min);
            }
        }
        else
        {
            Node *tmp = tree;
            tree = tree->left ? tree->left : tree->right;
            free(tmp);
        }
    }

    return tree;
}

/* 
 * 刪除結點(key是節點值),返回根節點
 *
 * 引數說明:
 *     tree AVL樹的根結點
 *     key 待刪除的結點的鍵值
 * 返回值:
 *     根節點
 */
Node* avltree_delete(AVLTree tree, Type key)
{
    Node *z; 

    if ((z = avltree_search(tree, key)) != NULL)
        tree = delete_node(tree, z);
    return tree;
}

注意關於AVL樹的"前序遍歷"、"中序遍歷"、"後序遍歷"、"最大值"、"最小值"、"查詢"、"列印"、"銷燬"等介面與"二叉查詢樹"基本一樣,這些操作在"二叉查詢樹"中已經介紹過了,這裡就不再單獨介紹了。當然,後文給出的AVL樹的完整原始碼中,有給出這些API的實現程式碼。這些介面很簡單,Please RTFSC(Read The Fucking Source Code)!

AVL樹的C實現(完整原始碼)

AVL樹的標頭檔案(avltree.h)

#ifndef _AVL_TREE_H_
#define _AVL_TREE_H_

typedef int Type;

typedef struct AVLTreeNode{
    Type key;                    // 關鍵字(鍵值)
    int height;
    struct AVLTreeNode *left;    // 左孩子
    struct AVLTreeNode *right;    // 右孩子
}Node, *AVLTree;

// 獲取AVL樹的高度
int avltree_height(AVLTree tree);

// 前序遍歷"AVL樹"
void preorder_avltree(AVLTree tree);
// 中序遍歷"AVL樹"
void inorder_avltree(AVLTree tree);
// 後序遍歷"AVL樹"
void postorder_avltree(AVLTree tree);

void print_avltree(AVLTree tree, Type key, int direction);

// (遞迴實現)查詢"AVL樹x"中鍵值為key的節點
Node* avltree_search(AVLTree x, Type key);
// (非遞迴實現)查詢"AVL樹x"中鍵值為key的節點
Node* iterative_avltree_search(AVLTree x, Type key);

// 查詢最小結點:返回tree為根結點的AVL樹的最小結點。
Node* avltree_minimum(AVLTree tree);
// 查詢最大結點:返回tree為根結點的AVL樹的最大結點。
Node* avltree_maximum(AVLTree tree);

// 將結點插入到AVL樹中,返回根節點
Node* avltree_insert(AVLTree tree, Type key);

// 刪除結點(key是節點值),返回根節點
Node* avltree_delete(AVLTree tree, Type key);

// 銷燬AVL樹
void destroy_avltree(AVLTree tree);


#endif

AVL樹的實現檔案(avltree.c)

/**
 * AVL樹(C語言): C語言實現的AVL樹。
 *
 * @author skywang
 * @date 2013/11/07
 */

#include <stdio.h>
#include <stdlib.h>
#include "avltree.h"

#define HEIGHT(p)    ( (p==NULL) ? -1 : (((Node *)(p))->height) )
#define MAX(a, b)    ( (a) > (b) ? (a) : (b) )

/*
 * 獲取AVL樹的高度
 */
int avltree_height(AVLTree tree)
{
    return HEIGHT(tree);
}

/*
 * 前序遍歷"AVL樹"
 */
void preorder_avltree(AVLTree tree)
{
    if(tree != NULL)
    {
        printf("%d ", tree->key);
        preorder_avltree(tree->left);
        preorder_avltree(tree->right);
    }
}


/*
 * 中序遍歷"AVL樹"
 */
void inorder_avltree(AVLTree tree)
{
    if(tree != NULL)
    {
        inorder_avltree(tree->left);
        printf("%d ", tree->key);
        inorder_avltree(tree->right);
    }
}

/*
 * 後序遍歷"AVL樹"
 */
void postorder_avltree(AVLTree tree)
{
    if(tree != NULL)
    {
        postorder_avltree(tree->left);
        postorder_avltree(tree->right);
        printf("%d ", tree->key);
    }
}

/*
 * (遞迴實現)查詢"AVL樹x"中鍵值為key的節點
 */
Node* avltree_search(AVLTree x, Type key)
{
    if (x==NULL || x->key==key)
        return x;

    if (key < x->key)
        return avltree_search(x->left, key);
    else
        return avltree_search(x->right, key);
}

/*
 * (非遞迴實現)查詢"AVL樹x"中鍵值為key的節點
 */
Node* iterative_avltree_search(AVLTree x, Type key)
{
    while ((x!=NULL) && (x->key!=key))
    {
        if (key < x->key)
            x = x->left;
        else
            x = x->right;
    }

    return x;
}

/* 
 * 查詢最小結點:返回tree為根結點的AVL樹的最小結點。
 */
Node* avltree_minimum(AVLTree tree)
{
    if (tree == NULL)
        return NULL;

    while(tree->left != NULL)
        tree = tree->left;
    return tree;
}
 
/* 
 * 查詢最大結點:返回tree為根結點的AVL樹的最大結點。
 */
Node* avltree_maximum(AVLTree tree)
{
    if (tree == NULL)
        return NULL;

    while(tree->right != NULL)
        tree = tree->right;
    return tree;
}

/*
 * LL:左左對應的情況(左單旋轉)。
 *
 * 返回值:旋轉後的根節點
 */
static Node* left_left_rotation(AVLTree k2)
{
    AVLTree k1;

    k1 = k2->left;
    k2->left = k1->right;
    k1->right = k2;

    k2->height = MAX( HEIGHT(k2->left), HEIGHT(k2->right)) + 1;
    k1->height = MAX( HEIGHT(k1->left), k2->height) + 1;

    return k1;
}

/*
 * RR:右右對應的情況(右單旋轉)。
 *
 * 返回值:旋轉後的根節點
 */
static Node* right_right_rotation(AVLTree k1)
{
    AVLTree k2;

    k2 = k1->right;
    k1->right = k2->left;
    k2->left = k1;

    k1->height = MAX( HEIGHT(k1->left), HEIGHT(k1->right)) + 1;
    k2->height = MAX( HEIGHT(k2->right), k1->height) + 1;

    return k2;
}

/*
 * LR:左右對應的情況(左雙旋轉)。
 *
 * 返回值:旋轉後的根節點
 */
static Node* left_right_rotation(AVLTree k3)
{
    k3->left = right_right_rotation(k3->left);

    return left_left_rotation(k3);
}

/*
 * RL:右左對應的情況(右雙旋轉)。
 *
 * 返回值:旋轉後的根節點
 */
static Node* right_left_rotation(AVLTree k1)
{
    k1->right = left_left_rotation(k1->right);

    return right_right_rotation(k1);
}

/*
 * 建立AVL樹結點。
 *
 * 引數說明:
 *     key 是鍵值。
 *     left 是左孩子。
 *     right 是右孩子。
 */
static Node* avltree_create_node(Type key, Node *left, Node* right)
{
    Node* p;

    if ((p = (Node *)malloc(sizeof(Node))) == NULL)
        return NULL;
    p->key = key;
    p->height = 0;
    p->left = left;
    p->right = right;

    return p;
}

/* 
 * 將結點插入到AVL樹中,並返回根節點
 *
 * 引數說明:
 *     tree AVL樹的根結點
 *     key 插入的結點的鍵值
 * 返回值:
 *     根節點
 */
Node* avltree_insert(AVLTree tree, Type key)
{
    if (tree == NULL) 
    {
        // 新建節點
        tree = avltree_create_node(key, NULL, NULL);
        if (tree==NULL)
        {
            printf("ERROR: create avltree node failed!\n");
            return NULL;
        }
    }
    else if (key < tree->key) // 應該將key插入到"tree的左子樹"的情況
    {
        tree->left = avltree_insert(tree->left, key);
        // 插入節點後,若AVL樹失去平衡,則進行相應的調節。
        if (HEIGHT(tree->left) - HEIGHT(tree->right) == 2)
        {
            if (key < tree->left->key)
                tree = left_left_rotation(tree);
            else
                tree = left_right_rotation(tree);
        }
    }
    else if (key > tree->key) // 應該將key插入到"tree的右子樹"的情況
    {
        tree->right = avltree_insert(tree->right, key);
        // 插入節點後,若AVL樹失去平衡,則進行相應的調節。
        if (HEIGHT(tree->right) - HEIGHT(tree->left) == 2)
        {
            if (key > tree->right->key)
                tree = right_right_rotation(tree);
            else
                tree = right_left_rotation(tree);
        }
    }
    else //key == tree->key)
    {
        printf("新增失敗:不允許新增相同的節點!\n");
    }

    tree->height = MAX( HEIGHT(tree->left), HEIGHT(tree->right)) + 1;

    return tree;
}

/* 
 * 刪除結點(z),返回根節點
 *
 * 引數說明:
 *     ptree AVL樹的根結點
 *     z 待刪除的結點
 * 返回值:
 *     根節點
 */
static Node* delete_node(AVLTree tree, Node *z)
{
    // 根為空 或者 沒有要刪除的節點,直接返回NULL。
    if (tree==NULL || z==NULL)
        return NULL;

    if (z->key < tree->key)        // 待刪除的節點在"tree的左子樹"中
    {
        tree->left = delete_node(tree->left, z);
        // 刪除節點後,若AVL樹失去平衡,則進行相應的調節。
        if (HEIGHT(tree->right) - HEIGHT(tree->left) == 2)
        {
            Node *r =  tree->right;
            if (HEIGHT(r->left) > HEIGHT(r->right))
                tree = right_left_rotation(tree);
            else
                tree = right_right_rotation(tree);
        }
    }
    else if (z->key > tree->key)// 待刪除的節點在"tree的右子樹"中
    {
        tree->right = delete_node(tree->right, z);
        // 刪除節點後,若AVL樹失去平衡,則進行相應的調節。
        if (HEIGHT(tree->left) - HEIGHT(tree->right) == 2)
        {
            Node *l =  tree->left;
            if (HEIGHT(l->right) > HEIGHT(l->left))
                tree = left_right_rotation(tree);
            else
                tree = left_left_rotation(tree);
        }
    }
    else    // tree是對應要刪除的節點。
    {
        // tree的左右孩子都非空
        if ((tree->left) && (tree->right))
        {
            if (HEIGHT(tree->left) > HEIGHT(tree->right))
            {
                // 如果tree的左子樹比右子樹高;
                // 則(01)找出tree的左子樹中的最大節點
                //   (02)將該最大節點的值賦值給tree。
                //   (03)刪除該最大節點。
                // 這類似於用"tree的左子樹中最大節點"做"tree"的替身;
                // 採用這種方式的好處是:刪除"tree的左子樹中最大節點"之後,AVL樹仍然是平衡的。
                Node *max = avltree_maximum(tree->left);
                tree->key = max->key;
                tree->left = delete_node(tree->left, max);
            }
            else
            {
                // 如果tree的左子樹不比右子樹高(即它們相等,或右子樹比左子樹高1)
                // 則(01)找出tree的右子樹中的最小節點
                //   (02)將該最小節點的值賦值給tree。
                //   (03)刪除該最小節點。
                // 這類似於用"tree的右子樹中最小節點"做"tree"的替身;
                // 採用這種方式的好處是:刪除"tree的右子樹中最小節點"之後,AVL樹仍然是平衡的。
                Node *min = avltree_maximum(tree->right);
                tree->key = min->key;
                tree->right = delete_node(tree->right, min);
            }
        }
        else
        {
            Node *tmp = tree;
            tree = tree->left ? tree->left : tree->right;
            free(tmp);
        }
    }

    return tree;
}

/* 
 * 刪除結點(key是節點值),返回根節點
 *
 * 引數說明:
 *     tree AVL樹的根結點
 *     key 待刪除的結點的鍵值
 * 返回值:
 *     根節點
 */
Node* avltree_delete(AVLTree tree, Type key)
{
    Node *z; 

    if ((z = avltree_search(tree, key)) != NULL)
        tree = delete_node(tree, z);
    return tree;
}

/* 
 * 銷燬AVL樹
 */
void destroy_avltree(AVLTree tree)
{
    if (tree==NULL)
        return ;

    if (tree->left != NULL)
        destroy_avltree(tree->left);
    if (tree->right != NULL)
        destroy_avltree(tree->right);

    free(tree);
}

/*
 * 列印"AVL樹"
 *
 * tree       -- AVL樹的節點
 * key        -- 節點的鍵值 
 * direction  --  0,表示該節點是根節點;
 *               -1,表示該節點是它的父結點的左孩子;
 *                1,表示該節點是它的父結點的右孩子。
 */
void print_avltree(AVLTree tree, Type key, int direction)
{
    if(tree != NULL)
    {
        if(direction==0)    // tree是根節點
            printf("%2d is root\n", tree->key, key);
        else                // tree是分支節點
            printf("%2d is %2d's %6s child\n", tree->key, key, direction==1?"right" : "left");

        print_avltree(tree->left, tree->key, -1);
        print_avltree(tree->right,tree->key,  1);
    }
}

AVL樹的測試程式(avltree_test.c) 

/**
 * C 語言: AVL樹
 *
 * @author skywang
 * @date 2013/11/07
 */
#include <stdio.h>
#include "avltree.h"

static int arr[]= {3,2,1,4,5,6,7,16,15,14,13,12,11,10,8,9};
#define TBL_SIZE(a) ( (sizeof(a)) / (sizeof(a[0])) )

void main()
{
    int i,ilen;
    AVLTree root=NULL;

    printf("== 高度: %d\n", avltree_height(root));
    printf("== 依次新增: ");
    ilen = TBL_SIZE(arr);
    for(i=0; i<ilen; i++)
    {
        printf("%d ", arr[i]);
        root = avltree_insert(root, arr[i]);
    }

    printf("\n== 前序遍歷: ");
    preorder_avltree(root);

    printf("\n== 中序遍歷: ");
    inorder_avltree(root);

    printf("\n== 後序遍歷: ");
    postorder_avltree(root);
    printf("\n");

    printf("== 高度: %d\n", avltree_height(root));
    printf("== 最小值: %d\n", avltree_minimum(root)->key);
    printf("== 最大值: %d\n", avltree_maximum(root)->key);
    printf("== 樹的詳細資訊: \n");
    print_avltree(root, root->key, 0);


    i = 8;
    printf("\n== 刪除根節點: %d", i);
    root = avltree_delete(root, i);

    printf("\n== 高度: %d", avltree_height(root));
    printf("\n== 中序遍歷: ");
    inorder_avltree(root);
    printf("\n== 樹的詳細資訊: \n");
    print_avltree(root, root->key, 0);

    // 銷燬二叉樹
    destroy_avltree(root);
}

AVL樹的C測試程式

AVL樹的測試程式執行結果如下:

== 依次新增: 3 2 1 4 5 6 7 16 15 14 13 12 11 10 8 9 
== 前序遍歷: 7 4 2 1 3 6 5 13 11 9 8 10 12 15 14 16 
== 中序遍歷: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 
== 後序遍歷: 1 3 2 5 6 4 8 10 9 12 11 14 16 15 13 7 
== 高度: 5
== 最小值: 1
== 最大值: 16
== 樹的詳細資訊: 
 7 is root
 4 is  7's   left child
 2 is  4's   left child
 1 is  2's   left child
 3 is  2's  right child
 6 is  4's  right child
 5 is  6's   left child
13 is  7's  right child
11 is 13's   left child
 9 is 11's   left child
 8 is  9's   left child
10 is  9's  right child
12 is 11's  right child
15 is 13's  right child
14 is 15's   left child
16 is 15's  right child

== 刪除根節點: 8
== 高度: 5
== 中序遍歷: 1 2 3 4 5 6 7 9 10 11 12 13 14 15 16 
== 樹的詳細資訊: 
 7 is root
 4 is  7's   left child
 2 is  4's   left child
 1 is  2's   left child
 3 is  2's  right child
 6 is  4's  right child
 5 is  6's   left child
13 is  7's  right child
11 is 13's   left child
 9 is 11's   left child
10 is  9's  right child
12 is 11's  right child
15 is 13's  right child
14 is 15's   left child
16 is 15's  right child

下面,我們對測試程式的流程進行分析!

1. 新建AVL樹
   新建AVL樹的根節點root。

 

2. 依次新增"3,2,1,4,5,6,7,16,15,14,13,12,11,10,8,9" 到AVL樹中,過程如下。
2.01 新增3,2
新增3,2都不會破壞AVL樹的平衡性。

 

2.02 新增1
新增1之後,AVL樹失去平衡(LL),此時需要對AVL樹進行旋轉(LL旋轉)。旋轉過程如下:

 

2.03 新增4
新增4不會破壞AVL樹的平衡性。

 

2.04 新增5
新增5之後,AVL樹失去平衡(RR),此時需要對AVL樹進行旋轉(RR旋轉)。旋轉過程如下:

 

2.05 新增6
新增6之後,AVL樹失去平衡(RR),此時需要對AVL樹進行旋轉(RR旋轉)。旋轉過程如下:

 

2.06 新增7
新增7之後,AVL樹失去平衡(RR),此時需要對AVL樹進行旋轉(RR旋轉)。旋轉過程如下:

 

2.07 新增16
新增16不會破壞AVL樹的平衡性。

 

2.08 新增15
新增15之後,AVL樹失去平衡(RL),此時需要對AVL樹進行旋轉(RL旋轉)。旋轉過程如下:

 

2.09 新增14
新增14之後,AVL樹失去平衡(RL),此時需要對AVL樹進行旋轉(RL旋轉)。旋轉過程如下:

 

2.10 新增13
新增13之後,AVL樹失去平衡(RR),此時需要對AVL樹進行旋轉(RR旋轉)。旋轉過程如下:

 

2.11 新增12
新增12之後,AVL樹失去平衡(LL),此時需要對AVL樹進行旋轉(LL旋轉)。旋轉過程如下:

 

2.12 新增11
新增11之後,AVL樹失去平衡(LL),此時需要對AVL樹進行旋轉(LL旋轉)。旋轉過程如下:

 

2.13 新增10
新增10之後,AVL樹失去平衡(LL),此時需要對AVL樹進行旋轉(LL旋轉)。旋轉過程如下:

 

2.14 新增8
新增8不會破壞AVL樹的平衡性。

 

2.15 新增9
但是新增9之後,AVL樹失去平衡(LR),此時需要對AVL樹進行旋轉(LR旋轉)。旋轉過程如下:

 


3. 列印樹的資訊
輸出下面樹的資訊:

 

前序遍歷: 7 4 2 1 3 6 5 13 11 9 8 10 12 15 14 16 
中序遍歷: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 
後序遍歷: 1 3 2 5 6 4 8 10 9 12 11 14 16 15 13 7 
高度: 5
最小值: 1
最大值: 16

 

4. 刪除節點8

刪除操作並不會造成AVL樹的不平衡。

 

刪除節點8之後,在列印該AVL樹的資訊。
高度: 5
中序遍歷: 1 2 3 4 5 6 7 9 10 11 12 13 14 15 16

參考

https://www.cnblogs.com/skywang12345/p/3576969.html