数据结构 -- 二叉查找树(C语言实现)

目录

一、树(Tree)

二、二叉树

1、概念

2、二叉树的存储方式

3、二叉树的遍历

三、二叉查找树

1、查找操作

2、插入操作

3、删除操作

4、修改操作

          5、节点清空和树销毁操作

四、支持重复数据的二叉查找树

1、每个节点存储多个对象数据

2、每个节点存储单个对象数据

五、二叉查找树C语言实现

1、数据结构

2、操作函数声明

3、具体实现

4、调试问题

、说明


一、树(Tree)

树是一种一种非线性表结构。如下图,A 节点就是 B 节点的父节点,B 节点是 A 节点的子节点。B、C、D 这三个节点的父节点是同一个节点,所以它们之间互称为兄弟节点。我们把没有父节点的节点叫作根节点,也就是图中的节点 E。我们把没有子节点的节点叫作叶子节点或者叶节点,比如图中的 G、H、I、J、K、L 都是叶子节点

  • 节点的高度(Height):节点到叶子节点的最长路径(边数),从下往上
  • 节点深度(Depth):根节点到这个节点的的最长路径(边数),从上往下
  • 节点的层(Level)数:节点的深度+1

数的高度:根节点的高度

二、二叉树

1、概念

每个节点最多只有2个子节点的树,这两个子节点分别为左子节点和右子节点。

满二叉树:叶子节点全部在最底层,除了叶子节点外,每个节点都有左右两个子节点

完全二叉树:叶子节点都在最底下两层,最后一层的叶子节点都靠左排列,并且除了最后一层,其他层的节点个数都要达到最大

2、二叉树的存储方式

基于链表的链式存储:每个节点需要额外的2个指针来存储左右节点地址

基于数组的顺序存储:每个节点不需要额外的子节点指针,左子节点存储在下标 2 * i 的位置,右子节点存储在 2 * i + 1的位置,i为节点的层数。但如果是非完全二叉树也会比较浪费空间。

3、二叉树的遍历

根据节点和他的左右节点遍历的先后顺序,可分为前序遍历、中序遍历、后续遍历

  • 前序遍历是指,对于树中的任意节点来说,先打印这个节点,然后再打印它的左子树,最后打印它的右子树。本身 ---> 左 ---> 右
  • 中序遍历是指,对于树中的任意节点来说,先打印它的左子树,然后再打印它本身,最后打印它的右子树。左 ---> 本身 ---> 右
  • 后序遍历是指,对于树中的任意节点来说,先打印它的左子树,然后再打印它的右子树,最后打印这个节点本身。 左 ---> 右 ---> 本身

三、二叉查找树

一种能够快速查找、插入、删除数据的特殊二叉树结构。二叉查找树要求,在树中的任意一个节点,其左子树中的每个节点的值,都要小于这个节点的值,而右子树节点的值都大于这个节点的值,非平衡的二叉树

1、查找操作

二叉查找树可以快速的查找最大节点和最小节点;按照中序遍历二叉查找树,可以输出有序的数据序列,时间复杂度为O(n),非常高效。

查找指定节点步骤:

第一步:先取根节点,如果根节点数据 = 要查找的数据,则直接返回,否则进入第二步

第二步:如果查找的数比根节点小,则在左子树中递归查找

第三部:如果查找的数比根节点大,则在右子数中递归查找

查找最小节点步骤:

遍历二叉树的左子树,直到左节点为NULL的节点即为最小节点

查找最大节点步骤:

遍历二叉树的右子树,直到右节点为NULL的节点即为最大节点

中序遍历步骤:

对于树中的任意节点来说,先拷贝它的左子树节点数据至双向链表中,然后再拷贝它本身节点数据双向链表中,最后拷贝它的右子树节点数据双向链表中。左 ---> 本身 ---> 右

2、插入操作

插入操作与查找过程类似,一般新插入的数据都是在叶子节点上;所以也是先取根节点

第一步:如果查找数据比节点数据大,且节点的右子节点为空,则将数据直接插入右子节点的位置;如果右子节点不为空,则再递归遍历右子数,查找插入位置。

第二步:如果查找数据比节点数据小,且节点的左子节点为空,则将数据直接插入左子节点的位置;如果左子节点不为空,则再递归遍历左子数,查找插入位置。

3、删除操作

先说明下:del为删除节点、f_del为删除节点的父节点、min为最小节点、f_min为最小节点的父节点

第一种情况:如果删除的节点没有子节点,只需要直接将删除节点父节点中,指向要删除的子节点(删除节点)置为空。如图中要删除的55

第二种情况:如果删除的节点只有一个子节点(只有左节点或只有右节点),则只需要更新删除节点的父节点中指向要删除节点的指针,让其指向要删除节点的子节点。如图中要删除的13,有4种细分情况:

1、del只有左节点,del为f_del的左节点,删除过程为:将f_del左节点指向del的左节点,再将del删除

2、del只有左节点,del为f_del的右节点,删除过程为:将f_del右节点指向del的左节点,再将del删除

3、del只有右节点,del为f_del的左节点,删除过程为:将f_del左节点指向del的右节点,再将del删除

4、del只有右节点,del为f_del的右节点,删除过程为:将f_del右节点指向del的右节点,再将del删除

第三种情况:如果删除的节点有两个子节点,则需要找到该节点的右子树中最小的节点,把他替换到要删除的节点上,然后删除这个最小节点。如图要删除的18,有有4种细分情况:

1、f_min就为del,min为叶子节点,删除过程为:先将min的数据替换到del,再将f_min的右节点指向min的右节点(NULL),最后删除min

2、f_min就为del,min还有右子树(不会再有左子树),删除过程为:先将min的数据替换到del,再将f_min的右节点指向min的右节点(!NULL),最后删除min

3、f_min不为del,min为叶子节点,删除过程为:先将min的数据替换到del,再将f_min的左节点指向min的右节点(NULL),最后删除min

4、f_min不为del,min还有右子树(不会再有左子树),删除过程为:先将min的数据替换到del,再将f_min的左节点指向min的右节点(!NULL),最后删除min

4、修改操作

修改操作与查找操作类似,在查找到相同key值的节点后,先释放节点指向的数据空间,然后修改节点数据。在修改过程中需要注意:

1、会先释放节点指向的就数据空间(这里如果是realloc更大的数据空间,容易造成指针泄露,且是不知道整个数据结构的大小的) 

2、修改的节点必须为新动态分配的空间

3、如果修改的节点有多个key值相同的节点,只会修改最新查找的一个

 

5、节点清空和树销毁操作

有的时候需要清空二叉树的所有节点保留树的句柄,再插入新的节点时不用再重新创建一颗新的树。或是不需要使用到树时时候需要释放所有内存,就需要将整颗树销毁,下次使用的时候再重新申请新的树。

使用递归的方法,可以将树的所有节点删除,递归的终止条件就是节点为空,递归公式为,递归查找到左子树和右子树均为空的树节点,先将该节点指向的数据删除(如果指向的数据为动态分配的需要在这里释放),再释放节点空间,最后将节点指向空(这里需要注意指针变量传参,如果要修改该指针变量的值即指针本身需要传入指针变量的地址,即使用双重指针才能!!!

 

 

四、支持重复数据的二叉查找树

在工程中使用二叉查找树时,用来存储单个数据的情况很少,多用来存储包含多个字段的对象,利用对象的某个字段作为key值来构建key-value的数据存储结构。在key-value的数据存储结构中,就不可避免的会有相同key值的情况,在插入新的节点时,如何存储相同key值的对象?大致有以下两种方法

1、每个节点存储多个对象数据

在二叉查找树的每一个节点使用链表来存储key值相同的对象,相对容易理解,但是这样的弊端就是二叉树的每个节点中至少需要多3个地址空间用来存储 链表的节点数据,比较浪费空间。

2、每个节点存储单个对象数据

在插入数据时,如果key值与二叉树的某个节点key值相同,则将数据插入到这个相同key值节点的右子树。与不支持重复数据二叉树插入操作相比,只需要将 > key值得节点插入到这个节点的右子数改为 >= 即可实现

在查找数据时,查找key值相同的节点后,并不停止查找,而是继续在右子树中查找,直到遇到叶子节点为止,然后将节点数据放入链表中,这样可以把key值相同的所有数据都找出来。

在删除数据时,删除节点只有左子树时则直接退出,因为没有右子树就不会有相同的key节点。删除节点右子树不为空 或 右子树节点为空但是key值相同 则继续查找相同key值的节点

 

五、二叉查找树C语言实现

1、数据结构

#define BS_TREE_MALLOC(size)    pvPortMalloc(size);
#define BS_TREE_REALLOC(p,size) rt_realloc(p,size);
#define BS_TREE_CALLOC(n,size)  calloc(n,size);
#define BS_TREE_FREE(p)         vPortFree(p);

struct bs_tree_node;
struct bs_tree;

/* 
 * 二叉查找树key比较, key_cmp:传入的要比较的key, key_becmp:被比较的key
 * 任意一个节点,其左子树中任意一个节点的值,都要小于这个节点的值
 * 任意一个几点,其右子数中任意一个节点的值,都要大于这个节点的值
 * 返回值 > 0 : key_cmp > key_becmp
 * 返回值 = 0 : key_cmp = key_becmp
 * 返回值 < 0 : key_cmp < key_becmp
*/
typedef int (*bstree_keycmp)(struct bs_tree *bstree, const void *key_cmp, const void *key_becmp);
/* 二叉树中的节点数据删除函数,如果插入节点为动态分配,则需要在该函数中释放节点空间 */
typedef int (*bstree_value_free)(struct bs_tree_node *node);

struct bs_tree_node
{
    void *key;
    void *value;
    struct bs_tree_node *left_child;  /*左子树*/
    struct bs_tree_node *right_child; /*右子树*/
};

struct bs_tree
{
    int num;                        /*二叉树中节点个数的总和*/
    struct bs_tree_node *root;      /*二叉树的根节点*/
    bstree_keycmp        keycmp;    /*二叉树key比较*/
    bstree_value_free    valuefree; /*二叉树节点数据删除*/
};

#define BSTREE_IS_EMPTY(tree) (tree->num == 0)

/*根据当前结构体元素的地址,获取到结构体首地址*/
//#define OFFSETOF(TYPE,MEMBER) ((unsigned int)&((TYPE *)0)->MEMBER)
//#define container(ptr,type,member) ({\
//  const typeof( ((type *)0)->member) *__mptr = (ptr);\
//  (type *) ((char *)__mptr - OFFSETOF(type,member));})

#define OFFSETOF(TYPE,MEMBER) ((unsigned int)&((TYPE *)0)->MEMBER)
#define container(ptr,type,member) ((type *) ((char *)ptr - OFFSETOF(type,member)))

2、操作函数声明

extern struct bs_tree *bs_tree_creat(bstree_keycmp keycmp, bstree_value_free valuefree);
extern struct bs_tree *bs_tree_creat_default(bstree_value_free valuefree);
extern int    bs_tree_insert(struct bs_tree *bstree, void *key, void *value);
extern int    bs_tree_delete(struct bs_tree *bstree, void *key);
extern int    bs_tree_modify(struct bs_tree *bstree, void *key, void *value);
extern int    bs_tree_search(struct bs_tree *bstree, void *key, struct double_list *dlist);
extern void * bs_tree_search_min(struct bs_tree *bstree);
extern void * bs_tree_search_max(struct bs_tree *bstree);
extern void   bs_tree_node_empty(struct bs_tree **bstree, struct bs_tree_node **node);
extern void   bs_tree_destroy   (struct bs_tree **bstree);

/*中序遍历二叉树,并将节点数据放入双向链表中,链表在使用完释放空间的时候,不能将节点数据空间删除*/
extern int bs_tree_inorder(struct bs_tree *bstree, struct bs_tree_node *node, struct double_list *dlist);

extern void bs_tree_sample(void);

3、具体实现

#include "algo_bs_tree.h"


/**
 * 二叉树key比较, key_cmp:传入的要比较的key, key_becmp:被比较的key
 * 
 * @return > 0 : key_cmp > key_becmp
 * @return = 0 : key_cmp = key_becmp
 * @return < 0 : key_cmp < key_becmp
 * 
 */
static int bstree_keycmp_default(struct bs_tree *bstree, const void *key_cmp, const void *key_becmp)
{
    return strcmp(key_cmp, key_becmp);
}

/**
 * 动态创建一个二叉查找树.
 * 
 * @return NULL:创建失败
 *        !NULL:创建成功
 */
struct bs_tree *bs_tree_creat(bstree_keycmp keycmp, bstree_value_free valuefree)
{
    struct bs_tree *bstree = NULL;

    if (keycmp == NULL)
        return NULL;

    /*申请二叉查找树结构空间*/
    bstree = BS_TREE_MALLOC(sizeof(*bstree));
    if (bstree == NULL)
        return NULL;

    bstree->num       = 0;
    bstree->keycmp    = keycmp;
    bstree->valuefree = valuefree;
    bstree->root      = NULL;
    
    return bstree;
}

/**
 * 使用默认 key比较函数 动态创建一个二叉查找树.
 * 
 * @return NULL:创建失败
 *        !NULL:创建成功
 */
struct bs_tree *bs_tree_creat_default(bstree_value_free valuefree)
{
    return bs_tree_creat(bstree_keycmp_default, valuefree);
}


/**
 * 向一个二叉查找树插入一个节点.支持相同key值的节点插入
 * 
 * @param bstree: 二叉查找树
 * @param key: 关键值
 * @param value: 节点数据
 * 
 * @return 0:插入成功
 *        -1:二叉查找树不存在 或 key为空 或 value为空
 *        -2:节点空间申请失败
 *        -3:插入失败
 */
int bs_tree_insert(struct bs_tree *bstree, void *key, void *value)
{
    struct bs_tree_node *new_node = NULL;
    struct bs_tree_node *f_node = NULL;
    int res = 0;

    if (bstree == NULL || key == NULL || value == NULL)
        return -1;

    /*申请二叉查找树节点空间*/
    new_node = (struct bs_tree_node*)BS_TREE_MALLOC(sizeof(*new_node));
    if (new_node == NULL)
        return -2;
    
    new_node->key = key;
    new_node->value = value;
    new_node->left_child = NULL;
    new_node->right_child = NULL;
    
    /*数为空,插入到根节点*/
    if (BSTREE_IS_EMPTY(bstree))
    {
        bstree->root = new_node;
        bstree->num ++;
        return 0;
    }
    else
    {
        f_node = bstree->root;
        while (f_node != NULL)
        {
            res = bstree->keycmp(bstree, key, f_node->key);
            if (res >= 0) /*去右子树中查找,支持相同key值的节点插入*/
            {
                if (f_node->right_child == NULL)
                {
                    f_node->right_child = new_node;
                    bstree->num ++;
                    return 0;
                }
                f_node = f_node->right_child;
            }
            else if (res < 0)/*去左子树中查找*/
            {
                if (f_node->left_child == NULL)
                {
                    f_node->left_child = new_node;
                    bstree->num ++;
                    return 0;
                }
                f_node = f_node->left_child;
            }
        }
    }

    return -3;
}


/**
 * 删除一个节点.
 * 
 * @param bstree: 二叉查找树
 * @param key: 删除节点关键值
 * 
 * @return 0:删除成功
 *        -1:二叉查找树不存在 或 key为空
 *        -2:节点不存在
 */
int bs_tree_delete(struct bs_tree *bstree, void *key)
{
    struct bs_tree_node *del_node = NULL;//要删除节点
    struct bs_tree_node *f_delnode = NULL;//要删除节点的父节点
    struct bs_tree_node *minnode = NULL;//最小节点
    struct bs_tree_node *f_minnode = NULL;//最小节点的父节点
    struct bs_tree_node *temp = NULL;
    int finsh_flg = 0, res = 0;

    if (bstree == NULL || key == NULL)
        return -1;
    
    /*查找要删除的节点*/
    del_node = bstree->root;
    while ((del_node != NULL) && ((res = bstree->keycmp(bstree, key, del_node->key)) != 0))
    {
        f_delnode = del_node;
        if (res > 0)
        {
            del_node = del_node->right_child;
        }
        else
        {
            del_node = del_node->left_child;
        }
    }

    /*要删除的节点不存在*/
    if (del_node == NULL)
        return -2;

    do
    {           
        if (bstree->keycmp(bstree, key, del_node->key) == 0)
        {
            /*如果删除的节点有两个子节点,则需要找到该节点的右子树中最小的节点,把他替换到要删除的节点上,然后删除这个最小节点
            *
            *1、f_min就为del,min为叶子节点,删除过程为:先将min的数据替换到del,再将f_min的右节点指向min的右节点(NULL),最后删除min
            *2、f_min就为del,min还有右子树(不会再有左子树),删除过程为:先将min的数据替换到del,再将f_min的右节点指向min的右节点(!NULL),最后删除min
            *3、f_min不为del,min为叶子节点,删除过程为:先将min的数据替换到del,再将f_min的左节点指向min的右节点(NULL),最后删除min
            *4、f_min不为del,min还有右子树(不会再有左子树),删除过程为:先将min的数据替换到del,再将f_min的左节点指向min的右节点(!NULL),最后删除min
            */
            if ((del_node->left_child != NULL) && (del_node->right_child != NULL))
            {
                f_minnode = del_node;
                minnode = del_node->right_child;
                while (minnode->left_child != NULL)//查找最小节点
                {
                    f_minnode = minnode;
                    minnode = minnode->left_child;
                }

                /*先删除要删除节点上的数据,数据空间为动态申请的需要在这里先释放*/
                bstree->valuefree(del_node);

                del_node->key = minnode->key;
                del_node->value = minnode->value;
                if (f_minnode == del_node)
                {
                    f_minnode->right_child = minnode->right_child;
                }
                else
                {
                    f_minnode->left_child = minnode->right_child;
                }

                /*交换最小节点和删除节点,因为真正删除的是最小节点,
                删除节点的父节点的子节点还是删除节点*/
                temp = minnode;
                minnode = del_node;
                del_node = temp;
            }
            /*要删除的节点只有左子树*/
            else if (del_node->left_child != NULL)
            {
                /*先删除要删除节点上的数据,数据空间为动态申请的需要在这里先释放*/
                bstree->valuefree(del_node);
                
                minnode = del_node->left_child;

                finsh_flg = 1;
            }
            /*要删除的节点只有右子树*/
            else if (del_node->right_child != NULL)
            {
                /*先删除要删除节点上的数据,数据空间为动态申请的需要在这里先释放*/
                bstree->valuefree(del_node);

                minnode = del_node->right_child;
            }
            /*要删除节点没有子节点*/
            else
            {
                /*先删除要删除节点上的数据,数据空间为动态申请的需要在这里先释放*/
                bstree->valuefree(del_node);

                minnode->left_child = NULL;
                minnode->right_child = NULL;
                minnode = NULL;
            }

            if (del_node == bstree->root)//删除的是头结点
            {
                bstree->root = minnode;
            }
            else
            {
                if (del_node == f_delnode->left_child)//删除节点在删除节点父节点的左子树上
                {
                    f_delnode->left_child = minnode;
                }
                else if (del_node == f_delnode->right_child)//删除节点在删除节点父节点的右子树上
                {
                    f_delnode->right_child = minnode;
                }
            }

            BS_TREE_FREE(del_node);
            del_node = minnode;/*将删除节点指向填补的节点,继续判断删除节点的右子树是否还有相同key值的节点*/

            bstree->num --;    
        }
        else
        {
            del_node = del_node->right_child;/*右子树不为空但key不相同*/
        }
                
    /*1、删除节点只有左子树时则直接退出,因为没有右子树就不会有相同的key节点
      2、删除节点右子树不为空 或 右子树节点为空但是key值相同 则继续查找相同key值的节点
    */
    } while ( ((del_node->right_child != NULL) || ((del_node->right_child == NULL) && (bstree->keycmp(bstree, key, del_node->key) == 0))) && !finsh_flg);
      
    return 0;
}

/**
 * 修改一个节点.注意事项:
 * 1、会先释放节点指向的就数据空间(这里如果是realloc更大的数据空间,容易造成指针泄露,且是不知道整个数据结构的大小的) 
 * 2、修改的节点必须为新动态分配的空间
 * 3、如果修改的节点有多个key值相同的节点,只会修改最新查找的一个
 *
 * @param bstree: 二叉查找树
 * @param key: 修改节点关键值
 * @param value: 修改节点数据
 * 
 * @return 0:修改成功
 *        -1:二叉查找树不存在 或 key为空 或value为空
 *        -2:节点不存在
 */
int bs_tree_modify(struct bs_tree *bstree, void *key, void *value)
{
    struct bs_tree_node *mody_node = NULL;
    int res = -2;
    
    if (bstree == NULL || key == NULL || value == NULL)
        return -1;

    /*查找要修改的节点*/
    mody_node = bstree->root;
    while (mody_node != NULL)
    {
        res = bstree->keycmp(bstree, key, mody_node->key);
        if (res > 0)
        {
            mody_node = mody_node->right_child;
        }
        else if (res < 0)
        {
            mody_node = mody_node->left_child;
        }
        else
        {
            bstree->valuefree(mody_node);   
            mody_node->key = key;
            mody_node->value = value;
            res = 0;
            break;
        }
    }

    return res;
}

/**
 * 根据key查找节点数据.
 * 
 * @param bstree: 二叉查找树
 * @param key: 查找节点关键值
 * 
 * @return 0:查找成功
 *        -1:二叉查找树不存在 或 key为空 或value为空
 *        -2:节点不存在
 */
int bs_tree_search(struct bs_tree *bstree, void *key, struct double_list *dlist)
{
    struct bs_tree_node *ser_node = NULL;
    int res = -2;
    
    if (bstree == NULL || key == NULL || BSTREE_IS_EMPTY(bstree))
        return -1;

    /*查找要查找的节点*/
    ser_node = bstree->root;
    while (ser_node != NULL)
    {
        res = bstree->keycmp(bstree, key, ser_node->key);
        if (res > 0)
        {
            ser_node = ser_node->right_child;
        }
        else if (res < 0)
        {
            ser_node = ser_node->left_child;
        }
        else
        {
            double_list_add_node_tail(dlist, ser_node->value);
            ser_node = ser_node->right_child;
            res = 0;
        }
    }

    return res;
}

/**
 * 查找二叉树中的最小节点数据.
 * 
 * @param bstree: 二叉查找树
 * 
 * @return 0:查找成功
 *        -1:二叉查找树不存在 或 key为空 或value为空
 *        -2:节点不存在
 */
void * bs_tree_search_min(struct bs_tree *bstree)
{
    struct bs_tree_node *ser_node = NULL;
    
    if (bstree == NULL || BSTREE_IS_EMPTY(bstree))
        return NULL;

    /*查找要查找的节点*/
    ser_node = bstree->root;
    while ((ser_node != NULL) && (ser_node->left_child != NULL))
    {
        ser_node = ser_node->left_child;
    }

    if (ser_node != NULL)
    {
        return ser_node->value;
    }

    return NULL;
}

/**
 * 查找二叉树中的最大节点数据.
 * 
 * @param bstree: 二叉查找树
 * 
 * @return 0:查找成功
 *        -1:二叉查找树不存在 或 key为空 或value为空
 *        -2:节点不存在
 */
void * bs_tree_search_max(struct bs_tree *bstree)
{
    struct bs_tree_node *ser_node = NULL;
    
    if (bstree == NULL || BSTREE_IS_EMPTY(bstree))
        return NULL;

    /*查找要查找的节点*/
    ser_node = bstree->root;
    while ((ser_node != NULL) && (ser_node->right_child != NULL))
    {
        ser_node = ser_node->right_child;
    }

    if (ser_node != NULL)
    {
        return ser_node->value;
    }

    return NULL;
}

/**
 * 中序遍历二叉树,并将节点数据放入双向链表中
 * 
 * @param bstree: 二叉查找树
 * 
 * @return NULL:查找失败
 *        !NULL:查找成功.返回节点数据
 */
int bs_tree_inorder(struct bs_tree *bstree, struct bs_tree_node *node, struct double_list *dlist)
{    
    if (bstree == NULL || BSTREE_IS_EMPTY(bstree))
        return NULL;

    if (node == NULL)
    {
        return NULL;
    }

    bs_tree_inorder(bstree, node->left_child, dlist);
    double_list_add_node_tail(dlist, node->value);
    bs_tree_inorder(bstree, node->right_child, dlist);

    return dlist->len;
}

///**
// * 清空二叉树节点数据
// * 
// * @param bstree: 二叉查找树
// * 
// * @return 
// */
//void bs_tree_node_empty(struct bs_tree *bstree, struct bs_tree_node *node)
//{
//    if (bstree == NULL || BSTREE_IS_EMPTY(bstree))
//        return;

//    if (node == NULL)
//    {
//        return;
//    }

//    bs_tree_node_empty(bstree, node->left_child);
//    bs_tree_node_empty(bstree, node->right_child);
//    bstree->valuefree(node);
//    BS_TREE_FREE(node);
//  node = NULL;
////    bstree->num--;
//}

///**
// * 销毁一颗二叉查找树
// * 
// * @param bstree: 二叉查找树
// * 
// * @return 
// */
//void bs_tree_destroy(struct bs_tree *bstree)
//{
//    bs_tree_node_empty(bstree, bstree->root);
//    BS_TREE_FREE(bstree);
//}

/**
 * 清空二叉树节点数据
 * 
 * @param bstree: 二叉查找树
 * 
 * @return 
 */
void bs_tree_node_empty(struct bs_tree **bstree, struct bs_tree_node **node)
{
    if (*bstree == NULL || BSTREE_IS_EMPTY((*bstree)))
        return;

    if (*node == NULL)
    {
        return;
    }

    bs_tree_node_empty(bstree, &(*node)->left_child);
    bs_tree_node_empty(bstree, &(*node)->right_child);
    (*bstree)->valuefree(*node);
    (*bstree)->num--;
    BS_TREE_FREE(*node);
    *node = NULL;
}

/**
 * 销毁一颗二叉查找树
 * 
 * @param bstree: 二叉查找树
 * 
 * @return 
 */
void bs_tree_destroy(struct bs_tree **bstree)
{
    bs_tree_node_empty(bstree, &(*bstree)->root);
    BS_TREE_FREE(*bstree);
    *bstree = NULL;
}

/*******************************************************************************************
 *                                          使用示例
 *******************************************************************************************/
struct test_node
{
    char key[10];
    char value[10];
};

static int node_value_free_sample(struct bs_tree_node *node)
{
    struct test_node *node_temp = NULL;

    /*根据key在test_node结构体中的偏移地址,找到二叉查找树节点实际指向的结构体首地址*/
    node_temp = container(node->key, struct test_node, key);
    /*如果节点所指向数据空间为动态申请的则需要释放*/
    BS_TREE_FREE(node_temp);
    /*将二叉树中指向这块内存的节点key 和 value 赋为空*/
    node->key = NULL;
    node->value = NULL;
    
    return 0;
}


struct bs_tree *bs_tree_test = NULL;
char tree_node_read[10][10];
struct double_list *dlist_test = NULL;

void bs_tree_sample(void)
{
    int i = 0, j = 0, key[10] = {0};
    struct test_node *node_temp = NULL;
    char rd_key[10] = {10}, del_key[10] = {0};
    struct double_list_node *dlist_node = NULL;

    bs_tree_test = bs_tree_creat_default(node_value_free_sample);
    dlist_test = double_list_creat();
    
    for (i=0; i<10; i++)
    {
        key[i] = rand() % 10;
    }
    
    /*插入 -- 查询*/
    for (i=0; i<10; i++)
    {
        node_temp = BS_TREE_MALLOC(sizeof(*node_temp));
        memset(node_temp, 0, sizeof(*node_temp));
        sprintf(node_temp->key, "AAA%d", key[i]);
        sprintf(node_temp->value, "%d", key[i]);
        bs_tree_insert(bs_tree_test, node_temp->key, node_temp->value);//支持多个节点插入
    }
    for (i=0; i<10; i++)
    {
        memset(tree_node_read[i], 0, 10);
        memset(rd_key, 0, sizeof(rd_key));
        sprintf(rd_key, "AAA%d", key[i]);
        
        bs_tree_search(bs_tree_test, rd_key, dlist_test);//相同key值的数据会被放入双向链表中
        dlist_node = dlist_test->head;
        for (j=0; j<dlist_test->len; j++)//相同key值的节点数据
        {
            memcpy(tree_node_read[j], dlist_node->value, 10);
            dlist_node = dlist_node->next;
        }
        double_list_node_empty(dlist_test, 0);//清空双向链表节点
    }
    
    /*中序遍历*/
    bs_tree_inorder(bs_tree_test, bs_tree_test->root, dlist_test);
    dlist_node = dlist_test->head;
    for (i=0; i<dlist_test->len; i++)
    {
        memcpy(tree_node_read[i], dlist_node->value, 10);
        dlist_node = dlist_node->next;
    }
    double_list_node_empty(dlist_test, 0);
    
    
    
    /*清空 -- 插入 -- 查询*/
    bs_tree_node_empty(&bs_tree_test, &(bs_tree_test)->root);
    for (i=0; i<10; i++)
    {
        node_temp = BS_TREE_MALLOC(sizeof(*node_temp));
        memset(node_temp, 0, sizeof(*node_temp));
        sprintf(node_temp->key, "AAA%d", key[i]);
        sprintf(node_temp->value, "%d", key[i] + 10);
        bs_tree_insert(bs_tree_test, node_temp->key, node_temp->value);
    }
    for (i=0; i<10; i++)
    {
        memset(tree_node_read[i], 0, 10);
        memset(rd_key, 0, sizeof(rd_key));
        sprintf(rd_key, "AAA%d", key[i]);
        
        bs_tree_search(bs_tree_test, rd_key, dlist_test);//相同key值的数据会被放入双向链表中
        dlist_node = dlist_test->head;
        for (j=0; j<dlist_test->len; j++)//相同key值的节点数据
        {
            memcpy(tree_node_read[j], dlist_node->value, 10);
            dlist_node = dlist_node->next;
        }
        double_list_node_empty(dlist_test, 0);//清空双向链表节点
    }
    /*中序遍历*/
    bs_tree_inorder(bs_tree_test, bs_tree_test->root, dlist_test);
    dlist_node = dlist_test->head;
    for (i=0; i<dlist_test->len; i++)
    {
        memcpy(tree_node_read[i], dlist_node->value, 10);
        dlist_node = dlist_node->next;
    }
    double_list_node_empty(dlist_test, 0);
    
    
    
    /*修改 -- 查询*/
    for (i=0; i<10; i++)
    {
        node_temp = BS_TREE_MALLOC(sizeof(*node_temp));
        memset(node_temp, 0, sizeof(*node_temp));
        sprintf(node_temp->key, "AAA%d", key[i]);
        sprintf(node_temp->value, "%d", key[i] + 20);
        bs_tree_modify(bs_tree_test, node_temp->key, node_temp->value);
    }
    for (i=0; i<10; i++)
    {
        memset(tree_node_read[i], 0, 10);
        memset(rd_key, 0, sizeof(rd_key));
        sprintf(rd_key, "AAA%d", key[i]);
        
        bs_tree_search(bs_tree_test, rd_key, dlist_test);//相同key值的数据会被放入双向链表中
        dlist_node = dlist_test->head;
        for (j=0; j<dlist_test->len; j++)//相同key值的节点数据
        {
            memcpy(tree_node_read[j], dlist_node->value, 10);
            dlist_node = dlist_node->next;
        }
        double_list_node_empty(dlist_test, 0);//清空双向链表节点
    }
    
    /*中序遍历*/
    bs_tree_inorder(bs_tree_test, bs_tree_test->root, dlist_test);
    dlist_node = dlist_test->head;
    for (i=0; i<dlist_test->len; i++)
    {
        memcpy(tree_node_read[i], dlist_node->value, 10);
        dlist_node = dlist_node->next;
    }
    double_list_node_empty(dlist_test, 0);

    
    
    
    
    /*删除 -- 查询*/
    sprintf(del_key, "AAA%d", key[0]);
    bs_tree_delete(bs_tree_test, del_key);
    for (i=0; i<10; i++)
    {
        memset(tree_node_read[i], 0, 10);
    }
    bs_tree_inorder(bs_tree_test, bs_tree_test->root, dlist_test);/*中序遍历*/
    dlist_node = dlist_test->head;
    for (i=0; i<dlist_test->len; i++)
    {
        memcpy(tree_node_read[i], dlist_node->value, 10);
        dlist_node = dlist_node->next;
    }
    double_list_node_empty(dlist_test, 0);
    /*删除 -- 查询*/
    sprintf(del_key, "AAA%d", key[1]);
    bs_tree_delete(bs_tree_test, del_key);
    for (i=0; i<10; i++)
    {
        memset(tree_node_read[i], 0, 10);
    }
    bs_tree_inorder(bs_tree_test, bs_tree_test->root, dlist_test);/*中序遍历*/
    dlist_node = dlist_test->head;
    for (i=0; i<dlist_test->len; i++)
    {
        memcpy(tree_node_read[i], dlist_node->value, 10);
        dlist_node = dlist_node->next;
    }
    double_list_node_empty(dlist_test, 0);
    /*删除 -- 查询*/
    sprintf(del_key, "AAA%d", key[2]);
    bs_tree_delete(bs_tree_test, del_key);
    for (i=0; i<10; i++)
    {
        memset(tree_node_read[i], 0, 10);
    }
    bs_tree_inorder(bs_tree_test, bs_tree_test->root, dlist_test);/*中序遍历*/
    dlist_node = dlist_test->head;
    for (i=0; i<dlist_test->len; i++)
    {
        memcpy(tree_node_read[i], dlist_node->value, 10);
        dlist_node = dlist_node->next;
    }
    double_list_node_empty(dlist_test, 0);
    
    
    /*释放二叉树 和 双向链表结构*/
    double_list_destroy(dlist_test, 0);
    bs_tree_destroy(&bs_tree_test);
}

4、调试问题

1、逻辑判断的运算符优先级要比赋值运算符优先级高

正确写法:while((del_node != NULL)&&((res = bstree->keycmp(bstree, key, del_node->key)) != 0))

错误写法:while ((del_node != NULL) && (res = bstree->keycmp(bstree, key, del_node->key) != 0))

2、指针变量传参时,如果要修改该指针变量的值即指针本身需要传入指针变量的地址,即使用双重指针才能。如本文中的 二叉树节点清空和二叉树销毁

六、说明

本文部分内容(图片与部分文字)为学习王争老师在极客时间专栏——《数据结构与算法之美》的学习总结。仅仅是个人学习备忘,如有侵犯权益,请联系我,立即修改。

发布了35 篇原创文章 · 获赞 22 · 访问量 1133

猜你喜欢

转载自blog.csdn.net/m0_37845735/article/details/103898314