C语言二叉树搜索(BST)相关练习

最近学习了二叉搜索树的相关知识,练习写了关于为二叉搜索树实现一个抽象数据类型(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;
}

本人新手小白,如果有什么还可以改进的地方,请大神多多指教。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值