1. 程式人生 > >二叉搜索樹的實現源碼(源碼較長,請慎入)

二叉搜索樹的實現源碼(源碼較長,請慎入)

直接 perf roo -- lan span 指向 blog balanced

實現二叉搜索樹的一種好方法是利用二叉樹抽象數據類型。

我們以BisTree這個名稱來代表二叉搜索樹這種數據結構通過typedef方式將BisTree(二叉搜索樹)實現為BiTree(二叉樹)的別名

采用typedef方法使得二叉搜索樹具有了某種程度的多態能力,如同棧和隊列一樣。這意味著除了專屬於二叉搜索樹的操作外,還可以在其上執行屬於二叉樹的操作。

數據結構AvlNode代表樹中的結點,AvlNode包含3個成員:

data是存儲在結點中的數據;hidden用來標識結點是否已經移除;factor則代表該結點的平衡因子

我們使用標識符來標識平衡因子可能的值:AVL_LEFT_HEAVY定義為1,AVL_BALANCED定義為0,AVL_RGT_HEAVY定義為-1

示例代碼1:二叉搜索樹抽象數據類型的頭文件

/*bistree.h*/
#ifndef BISTREE_H
#define BISTREE_H

#include "bitree.h"

/*定義平衡因子的標識符*/
#define AVL_LFT_HEAVY 1
#define AVL_BALANCED  0
#define AVL_RGT_HEAVY -1

/*定義AVL樹中節點的數據結構*/
typedef struct AvlNode_
{
    void *data;
    int hidden;
    int factor;
}AvlNode;

/*通過typedef方式將BisTree(二叉搜索樹)實現為BiTree(二叉樹)
*/ typedef BiTree BisTree; /*公用接口部分*/ void bistree_init(BisTree *tree,int (*compare)(const void *key1,const void *key2),void(*destroy)(void *data));
void bistree_destroy(BisTree *tree);
int bistree_insert(BisTree *tree,const void *data); int bistree_remove(BisTree *tree,const void *data); int bistree_lookup(BisTree *tree,void
**data); #define bistree_size(tree)((tree)->size) #endif // BISTREE_H

bistree_init

該操作用來初始化一棵二叉樹。由於二叉搜索樹實際上也是一棵二叉樹,因此調用bitree_init來完成初始化工作。這裏需要顯示地將compare成員設置為指定的比較函數,該成員在二叉樹中並沒有使用到。

bistree_init的時間復雜度和bitree_init一樣都是O(1)。

bistree_destroy

該操作用來銷毀一棵二叉樹。為了實現該操作,引入兩個額外的輔助函數destroy_left和destroy_right。這兩個函數按照遞歸的方式銷毀某個結點下方的左右子樹。針對二叉搜索樹,需要單獨的析構函數來銷毀AvlNode中數據成員所引用的空間以及AvlNode結構體本身所占用的資源。

bistree_destroy的時間復雜度同bitree_destroy一樣,都是O(n),這裏的n代表樹中的結點個數。

bistree_insert

該操作將一個結點插入二叉搜索樹中。該操作按照遞歸的方式調用bitree_insert來找到結點應該插入的位置。一旦插入結點,就需要更新相關結點的平衡因子,更新操作在遞歸的回歸過程中完成。如果在處理的過程中有任何結點的平衡因子絕對值為2,都需要執行一次旋轉。

從檢查是否將結點插入空樹開始,如果是這種情況,簡單的將結點設為根結點,並設它的平衡因子為AVL_BALANCED。

否則,將待插入結點的數據與當前結點的數據相比較,以此來判斷需要往哪個方向移動(左子樹還是右子樹)。當要插入的結點數據小於當前結點的數據時,遞歸調用該函數使我們移動到左邊。當待插入結點的數據大於當前結點的數據時,遞歸調用該函數將使我們移動到右邊。一旦找到了插入的位置,就動態分配一個AvlNode結構體,並將其插入樹中,作為當前結點的子結點。

如果待插入結點的數據恰好與某個隱藏的結點的數據一致(由於執行了移除操作,使結點成為隱藏的,但並未真正移除),就銷毀當前結點中的數據,將新的數據插入到原來的位置上,並標識該結點不再為隱藏的。在這種情況下不需要再平衡樹。

除此之外,替換了之前是隱藏的結點後,下一步需要評估樹的平衡性受到的影響,這樣如果有必要的話可以對其修復。不論結點插入左邊還是右邊,都用balanced來表示插入操作是否破壞了樹的平衡性。調整當前結點的平衡因子反過來可能會導致該結點上層的平衡因子被打亂。因此,每次調用insert操作時,如果balanced為0,就遍歷樹當前這一層的所有結點,並更新結點的平衡因子。一旦發現沒有結點需要更新了,就將balanced設置為1,以此表示之前的操作已經完成。

switch語句決定如何更新平衡因子,也決定何時應該執行旋轉操作。實際用來執行旋轉操作的函數要麽是rotate_left要麽是rotate_right,它們決定旋轉的類型:如果調用rotate_left則是LL或者LR;如果調用rotate_right則是RL或RR。由於旋轉操作會改變結點的平衡因子,因此每個旋轉函數也會修正平衡因子。

和插入一顆完全平衡的二叉樹中一樣,它們的時間復雜度都是O(lgn)。

結合下圖可以更好的理解更新平衡因子和執行旋轉操作:

技術分享圖片

bistree_remove

該操作將一個節點從二叉搜索樹中移除。但它只是將結點隱藏起來而不是真正的移除,我們稱之為“惰性移除”。

要隱藏一個結點,將AvlNode結構中的hidden成員設置為1。如果稍後插入同樣的數據,只需簡單的將hidden成員再次設置為0。

如果移除的結點數目很大,可能就需要考慮將結點真正的移除並重新調整樹的結構。

要定位隱藏的結點,以遞歸的方式調用hide,直到找到目標為止。一旦將結點隱藏了,就沒有必要重新平衡樹了,因為並沒有對樹的結構做任何修改。因此,直接將balanced值設置為1。

從AVL樹中移除結點的過程分析同插入結點一樣,因此bistree_remove的時間復雜度為O(lg n)。

從AVL移除結點的過程分析同插入結點一樣,因此,bistree_remove的時間復雜度為O(lgn)。

bistree_lookup

該操作用來在二叉搜索樹中查找結點。返回一個指向AvlNode結構體中數據域成員的指針。該操作通過遞歸的方式調用lookup,從根結點開始,逐層降低遍歷樹的結點,直到找到目標結點為止。在每個層級上,我們首先檢查是否到達了分支的邊緣。如果我們處於分支的邊緣就說明我們要找的結點不存在。否則,我們要麽移動到左子結點,要麽移動到右子結點,移動方式同前面介紹過的bistree_insert一樣。當我們遇到目標結點時,遞歸終結,此時返回0。

查找Avl樹中結點的分析過程同插入結點一樣,因此bistree_lookup的時間復雜度為O(lg n)。

bistree_size

這是一個宏,用來計算二叉搜索樹中結點的個數。直接訪問BisTree結構體中的size成員即可。

示例:二叉搜索樹抽象數據類型的實現(代碼較長,請做好心理準備)

/*bistree.c*/
#include <stdlib.h>
#include <string.h>
#include "bistree.h"   /*bistree.h中包含bitree.h*/

static void destroy_right(BisTree *tree,BiTreeNode *node);
/* rotate_left 執行LL或LR旋轉*/
static void rotate_left(BiTreeNode **node)
{
    BiTreeNode *left, *grandchild;
    /*設left為A的左子樹*/
    left = bitree_left(*node);
    /*left結點的平衡因子為1,說明新插入的結點位於A的左子樹的左子樹上*/
    if(((AvlNode *)bitree_data(left))->factor == AVL_LFT_HEAVY)
    {
        /*perform an LL rotation. 執行LL旋轉*/
        bitree_left(*node) = bitree_right(left);                 /*A的左指針指向left的右子結點*/
        bitree_right(left) = *node;                              /*left的右指針指向A*/
        ((AvlNode *)bitree_data(*node))->factor = AVL_BALANCED;  /*旋轉後,將A的平衡因子改為0*/
        ((AvlNode *)bitree_date(left))-factor = AVL_BALANCED;    /*旋轉後,將left的平衡因子改為0*/
        *node = left;                                            /*將原來指向A的指針指向left*/
    }
    /*left結點的平衡因子不為1,說明插入的結點位於A的左子樹的右子樹上*/
    else
    {
        /*perform an LR rotation. 執行LR旋轉*/
        grandchild = bitree_right(left);              /*設grandchild為left的右子結點*/
        bitree_right(left) = bitree_left(grandchild); /*將left的右子結點指向grch的左子結點*/
        bitree_left(grandchild) = left;               /*將grch的左子結點指向left*/
        bitree_left(*node) = bitree_right(grandchild);/*將A的左子結點指向grch的右子結點*/
        bitree_right(grandchild) = *node;             /*grch的右子結點指向A*/
        /*執行LR旋轉後,調整結點的平衡因子取決於旋轉前grch結點的原平衡因子值*/
        switch(((AvlNode *)bitree_data(grandchild))->factor)
        {
        case AVL_LFT_HEAVY: /*如grch原平衡因子值為1,就將A的平衡因子設為-1,left的設為0*/
            ((AvlNOde *)bitree_data(*node))->factor = AVL_RGT_HEAVY;
            ((AvlNode *)bitree_data(left))->factor = AVL_BALANCED;
            break;

        case AVL_BALANCED: /*如grch原平衡因子值為0,就將A的平衡因子設為0,left的設為0*/
            ((AvlNode *)bitree_data(*node))->factor = AVL_BALANCED;
            ((AvlNode *)bitree_data(left))->factor = AVL_BALANCED;
            break;

        case AVL_RGT_HEAVY: /*如grch原平衡因子值為-1,就將A的平衡因子設為0,left的設為1*/
            ((AvlNode *)bitree_data(*node))->factor = AVL_BALANCED;
            ((AvlNode *)bitree_data(left))->factor = AVL_LFT_HEAVY;
            break;
        }
        /*在所有情況下,grch的新平衡因子都為0*/
        ((AvlNode *)bitree_data(grandchild))->factor = AVL_BALANCED;
        /*將原來指向A的指針指向grch.*/
        *node = grandchild;
    }
    return;
}
/*rotate_right 執行LR旋轉*/
static void rotate_right(BiTreeNode **node)
{
    BiTreeNode *right, *grandchild;
    /*設right為A的右子樹*/
    right=bitree_right(*node);
    /*判斷right的平衡因子,-1代表新節點位於A的右子樹的右子樹上*/
    if(((AvlNode *)bitree_data(right))->factor == AVL_RGT_HEAVY)
    {
        /*perform an RR rotation. 執行RR旋轉*/
        bitree_right(*node)=bitree_left(right);                 /*將A的右子結點指向right的左子結點*/
        bitree_right(right)=*node;                              /*將right的左子結點指向A*/
        ((AvlNode *)bitree_data(*node))->factor = AVL_BALANCED; /*將A的平衡因子設為0*/
        ((AvlNode *)bitree_data(right))->factor = AVL_BALANCED; /*將right的平衡因子設為0*/
        *node = right;                                          /*原來指向A的指針指向right*/
    }
    else
    {
        /*perform an RL rotation. 執行RL旋轉*/
        grandchild = bitree_left(right);                /*設grch為right的左孩子*/
        bitree_left(right) = bitree_right(grandchild);  /*將right的左子結點指向grch的右子結點*/
        bitree_right(grandchild) = right;               /*將grch的右子結點指向right*/
        bitree_right(*node)=bitree_left(grandchild);    /*將A的右子結點指向grch的左子結點*/
        bitree_left(grandchild) = *node;                /*將grch的左子結點指向A*/
        /*執行RL旋轉後,調整結點的平衡因子取決於旋轉前grch的原平衡因子*/
        switch(((AvlNode *)bitree_data(grandchild))->factor)
        {
        case AVL_LFT_HEAVY:/*如grch原平衡因子值為1,就將A的平衡因子設為0,right的設為-1*/
            ((AvlNode *)bitree_data(*node))->factor = AVL_BALANCED;
            ((AvlNode *)bitree_data(*right))->factor = AVL_RGT_HEAVY;
            break;

        case AVL_BALANCED:/*如grch原平衡因子值為0,就將A的平衡因子設為0,right的設為0*/
            ((AvlNode *)bitree_data(*node))->facotr = AVL_BALANCED;
            ((AvlNode *)bitree_data(right))->factor = AVL_BALANCED;
            break;
        case AVL_RGT_HEAVY:/*如grch原平衡因子值為-1,就將A的平衡因子設為1,right的設為0*/
            ((AvlNode *)bitree_data(*node))->facotr = AVL_LFT_BALANCED;
            ((AvlNode *)bitree_data(right))->facotr = AVL_BALANCED;
            break;
        }
        /*在所有情況下,grch的新平衡因子都為0*/
        ((AvlNode *)bitree_data(grandchild))->factor = AVL_BALANCED;
        /*將原來指向A的指針指向grch*/
        *node = grandchild;
    }
    return;
}
/*destroy_left 銷毀左子樹*/
static void destroy_left(BisTree *tree,BiTreeNode *node)
{
BiTreeNode **position;

/*如果樹為空,直接返回*/
if(bitree_size(tree)==0)
return;
/*決定從何處銷毀子樹*/
if(node==NULL) /*node未指定,銷毀整棵樹*/
position = &tree->root;
else /*否則銷毀指定結點的左子樹*/
position = &node->left;
/*銷毀子樹*/
if(*position != NULL)
{
destroy_left(tree,*position); /*遞歸調用*/
destroy_right(tree,*position);

if(tree->destroy != NULL)
{
/*調用用戶定義函數來釋放動態分配的數據*/
tree->destroy(((AvlNode *)(*position)->data)->data);
}
/*釋放AVL數據節點,並釋放node結點本身*/
free((*position)->data);
free(*position);
*position = NULL;

/*調整樹的大小*/
tree->size--;
}
return;
}

/*destroy_right 銷毀右子樹*/
static void destroy_right(BisTree *tree,BiTreeNode *node)
{
BiTreeNode **position;
/*如果樹為空,直接返回*/
if(bitree_size(tree)==0)
return;

/*決定從何處銷毀樹*/
if(node==NULL) /*node未被指定時銷毀整顆樹*/
position=&tree->root;
else /*否則銷毀指定結點的右子樹*/
position=&node->right;

/*銷毀子樹*/
if(*position != NULL)
{
destroy_left(tree,*position);
destroy_right(tree,*position); /*遞歸調用*/

if(tree->destroy != NULL)
{
/*調用用戶定義函數來釋放動態分配的數據*/
tree->destroy(((AvlNode *)(*position)->data)->data);
}

/*釋放AVL數據節點,並釋放node結點本身*/
free((*position)->data);
free(*position);

/*重置*position*/
*position = NULL;

/*調整樹的大小*/
tree->size--;
}
return;
}

/*insert 插入操作*/

static int insert(BisTree *tree,BiTreeNode **node,const void *data,int *balanced)
{
AvlNode *avl_data;
int cmpval,retval;

/*將數據插入到樹中*/

/*如果*node是分支的結束,即*node=NULL*/
if(bitree_is_eob(*node))
{
/*操作插入到空樹中,設置結點的AVL屬性值*/
if((avl_data = (AvlNode *)malloc(sizeof(AvlNode)))==NULL)
return -1;

avl_data->factor = AVL_BALANCED;
avl_data->hidden = 0;
avl_data->data = (void *)data;
/*將數據插入為根結點*/
return bitree_ins_left(tree,*node,avl_data);
}

else

{

/*控制插入到非空樹中 將待插入數據與當前結點的數據相比較*/

cmpval = tree->compare(data,((AvlNode *)bitree_data(*node))->data);

/*待插入結點的數據小於當前結點的數據*/

if(cmpval < 0)

{

/*向當前結點的左子樹移動*/

/*如果當前結點的左子樹不存在*/

if(bitree_is_eob(bitree_left(*node)))

{

if((avl_data = (AvlNode *)malloc(sizeof(AvlNode)))==NULL)

return -1;

/*將待插入結點插入到當前結點左側,並設置AVL屬性值*/

avl_data->factor=AVL_BALANCED;

avl_data->hidden=0;

avl_data->data=(void *)data;

if(bitree_ins_left(tree,*node,avl_data)!=0)

return -1;

*balanced=0;

}

/*如果當前結點的左子樹不為空,遞歸調用insert向下插入*/

else

{

if((retval = insert(tree,&bitree_left(*node),data,balanced))!=0)

retrun retval;

}

}

/*確保樹仍保持平衡*/

if(!(*balanced))

{

switch(((AvlNode *)bitree_data(*node))->factor)

{

case AVL_LFT_HEAVY:

rotate_left(node);

*balanced = 1;

break;

case AVL_BALANCED:

((AvlNode *)bitree_data(*node))->factor = AVL_LFT_HEAVY;

break;

case AVL_RGT_HEAVY:

((AvlNode *)bitree_data(*node))->factor = AVL_BALANCED;

*balanced = 1;

break;

}

}

\*如果待插入結點的值大於當前結點的值*\

else if (cmpval>0)

{

/*向當前結點的右子樹移動*/

/*如果當前結點的右子樹不存在*/

if(bitree_is_eob(bitree_right(*node)))

{

if((avl_data = (AvlNode *)malloc(sizeof(AvlNode)))==NULL)

return -1;

/*將待插入結點插入到當前結點右側,並設置AVL屬性值*/

avl_data->factor = AVL_BALANCED;

avl_data->hidden = 0;

avl_data->data = (void *)data;

if(bitree_ins_right(tree,*node,avl_data)!=0)

return -1;

*balanced = 0;

}

/*如果當前結點的右子樹不為空,遞歸調用insert向下插入*/

else

{

if(((retval = insert(tree,&bitree_right(*node),data,balanced))!=0)

return retval;

}

}

/*確定樹仍然是保持平衡的*/

if(!(*balanced))

{

switch (((AvlNode *)bitree_data(*node))->factor)

{

case AVL_LFT_HEAVY:

((AvlNode *)bitree_data(*node))->factor = AVL_BALANCED;

*balanced = 1;

break;

case AVL_BALANCED:

((AvlNode*)bitree_data(*node))->factor = AVL_RGT_HEAVY;

break;

case AVL_RGT_HEAVY:

rotate_right(node);

*balanced=1;

}

}

}

/*待插入結點與當前結點(惰性隱藏)相等*/

else

{

if(!(AvlNode *)bitree_data(*node))->hidden)

{

return -1;

}

else

{

/*銷毀當前結點中的數據*/

if(tree_destroy!=NULL)

{

tree->destroy(((AvlNode *)bitree_data(*node))->data);

}

/*將新數據插入到原來結點的位置上*/

((AvlNode *)bitree_data(*node))->data = (void *)data;

/*標識該結點不再為隱藏*/

((AvlNode *)bitree_data(*node))->hidden = 0;

/*這種情況下不需要再平衡樹*/

*balanced = 1;

}

}

}

return 0;

}

/*hide 惰性移除*/

static int hide(BisTree *tree,BiTreeNode *node,const void *data)

{

int cmpval,retval;

if(bitree_is_eob(node))

{

/*數據沒有被找到*/

return -1;

}

/*將待移除數據與當前結點數據相比較*/

cmpval = tree->compare(data,((AvlNode*)bitree_data(node))->data);

if(cmpval < 0)

{

/*向左移動*/

retval = hide(tree,bitree_left(node),data);

}

else if(cmpval > 0)

{

/*向右移動*/

retval = hide(tree,bitree_right(node),data);

}

else

{

/*兩個數據相等,將結點隱藏*/

((AvlNode *)bitree_data(node))->hidden = 1;

retval = 0;

}

return retval;

}

/*lookup 查找二叉樹中的結點*/

static int lookup(BisTree *tree,BiTreeNode *node,void **data)

{

int cmpval,retval;

if(bitree_is_eob(node))

{

/*數據結點沒有找到*/

return -1;

}

cmpval = tree->compare(*data,((AvlNode *)bitree_data(node))->data);

if(cmpval < 0)

{

/*向左移動,遞歸查詢*/

retval = lookup(tree,bitree_left(node),data);

}

else if(cmpval > 0)

{

/*向右移動,遞歸查詢*/

retval = lookup(tree,bitree_right(node),data);

}

else

{

if(!(AvlNode *)bitree_data(node))->hidden)

{

/*找到結點且未被隱藏,從樹中返回該結點數據*/

*data = ((AvlNode *)bitree_data(node))->data);

return 0;

}

else

{

/*如已隱藏,返回數據未被找到*/

return -1;

}

}

return retval;

}

/*bistree_init 初始化二叉搜索樹*/
void bistree_init(BisTree *tree,int(*compare)(const void *key1,const void *key2),void (*destroy)(void *data))
{
bitree_init(tree,destroy);
tree->compare=compare;

return;
}

/*bistree_destroy 銷毀二叉搜索樹*/

void bistree_destroy(BisTree *tree)

{

/*Destroy all nodes in the tree.*/

destroy_left(tree,NULL);

/*No operations are allowed now,but clear the structure as a precaution.*/

memset(tree,0,sizeof(BisTree));

return;

}

/*bistree_insert 向二叉樹中插入結點*/

int bistree_insert(BisTree *tree,const void *data)

{

int balanced=0;

return insert{tree,&bitree_root(tree),data,&balanced);

}

/*bistree_remove 惰性移除*/

int bistree_remove(BisTree *tree,const void *data)

{

return hide(tree,bitree_root(tree),data);

}

二叉搜索樹的實現源碼(源碼較長,請慎入)