目录
六、说明
一、树(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、指针变量传参时,如果要修改该指针变量的值即指针本身需要传入指针变量的地址,即使用双重指针才能。如本文中的 二叉树节点清空和二叉树销毁
六、说明
本文部分内容(图片与部分文字)为学习王争老师在极客时间专栏——《数据结构与算法之美》的学习总结。仅仅是个人学习备忘,如有侵犯权益,请联系我,立即修改。