最近学习了二叉搜索树的相关知识,练习写了关于为二叉搜索树实现一个抽象数据类型(ADT)的树(BST)。 BST以有序的方式存储密钥(左边的“更小”,右边的“更大的”根节点的权利)。 ADT的用户如何定义“更小”和“更大”界定。 没有关键词(key words)可以被存储两次,这里使用的BST不存储特定的类型,而是存储空间块指针。 比较两个不同的内存块的方式,比较或排序,是通过一个用户提供的功能,与qsort类似。功能:初始化,插入,最深路径,打印二叉树,中序遍历二叉树,重新平衡二叉树,释放二叉树缓存(为了避免内存遗漏Memory leak)
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include "bst.h"
bst* bst_init(int sz,
int(*comp)(const void* a, const void* b),
char*(*prnt)(const void* a) )
{
bst *l;
l = (bst*) calloc (1, sizeof(bst));
if(l == NULL){
ON_ERROR("创建二叉树失败\n");
}
l->top = NULL;
l->elsz = sz;
l->compare = comp;
l->prntnode = prnt;
return l;
}
/*二叉树初始化*/
bstnode* createNode(bst *b)
{
bstnode *l;
l= (bstnode*) calloc (1, sizeof(bstnode));
if(l == NULL){
ON_ERROR("创建节点失败.\n");
}
l->data = (bstnode*) calloc (1, b->elsz);
l->left = NULL;
l->right = NULL;
return l;
}
void insertBinTree(bst* b, bstnode *n, void* v)
{
if((b->compare)(v,n->data) == 0) {
return;
}else if((b->compare)(v,n->data) < 0){
if(n->left == NULL){
n->left = createNode(b);
memcpy(n->left->data, v, b->elsz);
}else{
insertBinTree(b, n->left, v);
}
}else{
if(n->right == NULL){
n->right = createNode(b);
memcpy(n->right->data, v, b->elsz);
}else{
insertBinTree(b, n->right, v);
}
}
}
void bst_insert(bst* b, void* v)
{
if(b == NULL){
return;
}else{
if(b->top == NULL){
b->top = createNode(b);
memcpy(b->top->data, v, b->elsz);
}else{
insertBinTree(b, b->top, v);
}
}
}
/* 二叉树中总结点计算 */
int node_sum(bstnode *n)
{
int sum;
sum = 0;
if(n == NULL){
return sum;
}else{
sum++;
return (sum + node_sum(n->left) + node_sum(n->right));
}
}
int bst_size(bst* b)
{
if(b==NULL || b->top == NULL){
return 0;
}
return node_sum(b->top);
}
/* 最深路径 */
int node_depth(bstnode *n)
{
int leftdep = 0;
int rightdep = 0;
if(n->left != NULL){
leftdep = node_depth(n->left);
}
if(n->right != NULL){
rightdep = node_depth(n->right);
}
return (leftdep > rightdep) ? leftdep+1 : rightdep+1;
}
int bst_maxdepth(bst* b)
{
if(b== NULL|| b->top == NULL){
return 0;
}
return node_depth(b->top);
}
/* 搜索void v 是否存在二叉树中 */
int inTree(bst* b, bstnode* n, void* v)
{
if(n == NULL){
return false;
}
if((b->compare)(v,n->data) == 0){
return true;
}
if((b->compare)(v,n->data) < 0){
return inTree(b, n->left, v);
}
else{
return inTree(b, n->right, v);
}
}
bool bst_isin(bst* b, void* v)
{
if(b == NULL || b->top == NULL){
return false;
}
return inTree(b, b->top, v);
}
/*把n个数组插入二叉树中去*/
void bst_insertarray(bst* b, void* v, int n)
{
int i;
char *p;
p = v;
if(b == NULL || v == NULL){
return;
}
for(i = 0; i < n; i++){
bst_insert(b, p + i*b->elsz);
}
}
/* 清除二叉树中的缓存*/
void node_free(bstnode *n)
{
if(n != NULL){
node_free(n->left);
node_free(n->right);
free(n->data);
free(n);
n = NULL;
}
}
void bst_free(bst** p)
{
bst* a = *p;
node_free(a->top);
free(a);
*p = NULL;
}
/***************************/
/* 附加功能 */
/***************************/
/*打印二叉树*/
char* printbst(bst* b, bstnode* n, char* str)
{
char *nstr;
char *data;
if(n != NULL){
data = b->prntnode(n->data);
nstr = (char *) calloc (1, strlen(data) +2);
if(nstr == NULL){
ON_ERROR("打印二叉树失败\n");
}
sprintf(nstr,"(%s", data);
strcat(str, nstr);
free(nstr);
if(n->left != NULL){
printbst(b, n->left, str);
}
if(n->right != NULL){
printbst(b, n->right, str);
}
strcat(str,")");
}
return str;
}
char* bst_print(bst* b)
{
char *str;
char *data;
if(b->top != NULL){
data = b->prntnode(b->top->data);
str = (char*) calloc (bst_size(b), (strlen(data)+2)+1);
return printbst(b, b->top, str);
free(str);
}else{
ON_ERROR("该二叉树为空\n");
}
}
/*中序遍历二叉树并赋值到数组中去*/
void inorder(bst* b, bstnode* n, void* v, int *count)
{
if(n != NULL){
inorder(b, n->left, v, count);
memcpy((char*)v+(*count)*b->elsz, n->data, b->elsz);
(*count)++;
inorder(b, n->right, v, count);
}
}
/* 按顺序读取二叉树并存在数组中去*/
void bst_getordered(bst* b, void* v)
{
int *count;
count=(int*) calloc (1, sizeof(int));
if(count == NULL){
ON_ERROR("创建计数器失败.\n");
}
*count = 0;
if(b == NULL || v == NULL){
return;
}else{
inorder(b, b->top, v, count);
}
free(count);
}
/*找到二叉树的中间节点*/
void middle_node(bst* l, void* v, int left, int right)
{
int middle;
if(left!=right){
middle = (left + right)/2;
bst_insert(l, (char*)v + (middle * l->elsz));
middle_node(l, v, left, middle);
middle_node(l, v, middle + 1, right);
}
}
/* 重平衡二叉树*/
bst* bst_rebalance(bst* b)
{
int size;
void *v;
bst* l;
l = bst_init(b->elsz, b->compare, b->prntnode);
if(b !=NULL){
size = bst_size(b);
v = (void *) calloc (1, size*b->elsz);
if(v == NULL){
ON_ERROR("重新排列二叉树失败.\n");
}else{
bst_getordered(b, v);
middle_node(l, v, 0, size);
free(v);
}
}
return l;
}
在这里也贴出bst.h的文件
#ifndef __BSTREE_H__
#define __BSTREE_H__
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#define ON_ERROR(STR) fprintf(stderr, STR); exit(EXIT_FAILURE)
/*动态(不平衡)二进制搜索树,存储
(void)指向数据的指针。 没有重复的东西可以存储*/
struct bstnode {
void* data;
struct bstnode* left;
struct bstnode* right;
};
typedef struct bstnode bstnode;
struct bst {
bstnode* top;
/* 数据元素大小,以字节为单位*/
int elsz;
/* 就像qsort()一样,传递一个可以比较两个数据项的函数*/
int(*compare)(const void* a, const void* b);
/* 传递一个可以将数据项返回为字符串的函数(用于打印)*/
char*(*prntnode)(const void* a);
};
typedef struct bst bst;
bst* bst_init(int sz,
int(*comp)(const void* a, const void* b),
char*(*prnt)(const void* a) );
/*在二叉树中插入数据*/
void bst_insert(bst* b, void* v);
/*二叉树中的节点数*/
int bst_size(bst* b);
/* 从根到任何叶的最长路径 */
int bst_maxdepth(bst* b);
/*v中的数据是否存储在树中 */
bool bst_isin(bst* b, void* v);
/* 批量插入数组v中的n个项目到初始化树中 */
void bst_insertarray(bst* b, void* v, int n);
/* 清除与树关联的所有内存,并将指针设置为NULL */
void bst_free(bst** p);
/***************************/
/*附加功能 */
/***************************/
/* 返回以文本形式(头(左)(右))递归显示树的字符串*/
char* bst_print(bst* b);
/* 用排序树数据的副本填充数组*/
void bst_getordered(bst* b, void* v);
/* 重新平衡树,递归使用排序键的中位数*/
bst* bst_rebalance(bst* b);
#endif
对应的测试文件testbst.c, 用assert()函数来测试具体代码内容
#include "bst.h"
#include <time.h>
#include <assert.h>
#define STRSIZE 20
#define WORDS 23
int mystrcmp(const void* a, const void* b);
int mychrcmp(const void* a, const void* b);
int myintcmp(const void* a, const void* b);
char* myprintstr(const void* v);
char* myprintchr(const void* v);
char* myprintint(const void* v);
void test_simpleinsert(void);
void test_insertarray(void);
void test_getordered(void);
void test_print(void);
void test_rebalance(void);
int main(void)
{
printf("Beginning BST Test ...\n");
test_simpleinsert();
test_insertarray();
test_getordered();
test_rebalance();
test_print();
printf("Finished BST Test ...\n");
return 0;
}
void test_simpleinsert(void)
{
char str[STRSIZE];
bst* b;
b = bst_init(STRSIZE, mystrcmp, myprintstr);
assert(bst_size(b)==0);
strcpy(str, "dog");
bst_insert(b, str);
assert(bst_size(b)==1);
assert(bst_isin(b, str));
strcpy(str, "apple");
bst_insert(b, str);
assert(bst_size(b)==2);
assert(bst_isin(b, str));
strcpy(str, "garage");
bst_insert(b, str);
assert(bst_size(b)==3);
assert(bst_isin(b, str));
strcpy(str, "baggage");
bst_insert(b, str);
assert(bst_size(b)==4);
assert(bst_isin(b, str));
strcpy(str, "baggage");
bst_insert(b, str);
assert(bst_size(b)==4);
assert(bst_isin(b, str));
strcpy(str, "igloo");
bst_insert(b, str);
assert(bst_size(b)==5);
assert(bst_isin(b, str));
strcpy(str, "cat");
bst_insert(b, str);
assert(bst_size(b)==6);
assert(bst_isin(b, str));
strcpy(str, "eccentric");
bst_insert(b, str);
assert(bst_size(b)==7);
assert(bst_isin(b, str));
strcpy(str, "fresian");
bst_insert(b, str);
assert(bst_size(b)==8);
assert(bst_isin(b, str));
strcpy(str, "hotel");
bst_insert(b, str);
assert(bst_size(b)==9);
assert(bst_isin(b, str));
strcpy(str, "jaguar");
bst_insert(b, str);
assert(bst_size(b)==10);
assert(bst_isin(b, str));
assert(bst_maxdepth(b)==4);
bst_free(&b);
assert(b==NULL);
}
void test_insertarray(void)
{
char words1[WORDS][STRSIZE] = {"it", "is", "a", "truth", "universally", "acknowledged", "that", "a", "single", "man", "in", "possession", "of", "a", "good", "fortune", "must", "be", "in", "want", "of", "a", "wife"};
bst* b = bst_init(STRSIZE, mystrcmp, myprintstr);
bst_insertarray(b, words1, WORDS);
assert(bst_size(b)==18);
bst_free(&b);
assert(b==NULL);
}
void test_getordered(void)
{
int i, sc;
char words1[WORDS][STRSIZE] = {"it", "is", "a", "truth", "universally", "acknowledged", "that", "a", "single", "man", "in", "possession", "of", "a", "good", "fortune", "must", "be", "in", "want", "of", "a", "wife"};
char words2[WORDS][STRSIZE];
bst* b = bst_init(STRSIZE, mystrcmp, myprintstr);
bst_insertarray(b, words1, WORDS);
assert(bst_size(b)==18);
bst_getordered(b, words2);
for(i=0; i<17; i++){
sc = strcmp(words2[i], words2[i+1]);
assert(sc<0);
}
bst_free(&b);
assert(b==NULL);
}
void test_rebalance(void)
{
int i;
bst* b = bst_init(sizeof(int), myintcmp, myprintint);
bst* rb;
/* Sorted List, bad for building tree */
for(i=0; i<2048; i++){
bst_insert(b, &i);
}
assert(bst_maxdepth(b)==i);
rb = bst_rebalance(b);
assert(bst_maxdepth(rb)==12);
bst_free(&b);
bst_free(&rb);
}
void test_print(void)
{
int i, sc;
char istr[] = "MNKIDFGH";
char pstr[] = "(M(K(I(D(F(G(H))))))(N))";
char* ostr;
bst* b;
b = bst_init(sizeof(char), mychrcmp, myprintchr);
assert(b!=NULL);
assert(bst_size(b)==0);
for(i=0; i<(int)strlen(istr); i++){
bst_insert(b, &istr[i]);
assert(bst_isin(b,&istr[i]));
assert(bst_size(b)==(i+1));
}
ostr = bst_print(b);
sc = strcmp(pstr,ostr);
assert(sc==0);
free(ostr);
bst_free(&b);
}
char* myprintstr(const void* v)
{
return (char*)v;
}
char* myprintchr(const void* v)
{
static char str[100];
sprintf(str, "%c", *(char*)v);
return str;
}
char* myprintint(const void* v)
{
static char str[100];
sprintf(str, "%d", *(int*)v);
return str;
}
int mystrcmp(const void* a, const void* b)
{
return strcmp(a, b);
}
int mychrcmp(const void* a, const void* b)
{
return *(char*)a - *(char*)b;
}
int myintcmp(const void* a, const void* b)
{
return *(int*)a - *(int*)b;
}
本人新手小白,如果有什么还可以改进的地方,请大神多多指教。