引文

  今天分享一个喜欢佩服的伟人,应该算人类文明极大突破者.收藏过一张纸币类型如下

那我们继续科普一段关于他的简介

  '高斯有些孤傲,但令人惊奇的是,他春风得意地度过了中产阶级的一生,而 
没有遭受到冷酷现实的打击;这种打击常无情地加诸于每个脱离现实环境生活的 
人。或许高斯讲求实效和追求完美的性格,有助于让他抓住生活中的简单现实。 
高斯22岁获博士学位,25岁当选圣彼德堡科学院外籍院士,30岁任哥廷根大学数 
学教授兼天文台台长。虽说高斯不喜欢浮华荣耀,但在他成名后的五十年间,这 
些东西就像雨点似的落在他身上,几乎整个欧洲都卷入了这场授奖的风潮,他一 
生共获得75种形形色色的荣誉,包括1818年英王乔治三世赐封的“参议员”, 
1845年又被赐封为“首席参议员”。高斯的两次婚姻也都非常幸福,第一个妻子 
死于难产后,不到十个月,高斯又娶了第二个妻子。心理学和生理学上有一个常 
见的现象,婚姻生活过得幸福的人,常在丧偶之后很快再婚,他的晚年不幸福,

孩子和他关系不好...'

  关于他的专业知识 业界评价如下

  '能从九霄云外的高度按照某种观点掌握星空和深奥数学的天才。'地球上搞数学人类中公认前三diao.

有时候我们所有的一切 都是自己抉择的过程,

不是自己选择,就是别人选择. 很公平, 就看每个人觉醒的早晚,觉醒能力的 不同而已.

推荐参照

    没有什么不同 http://music.163.com/#/song?id=25713024

再扯一点, '孤傲'的话题, 生活中有时候遇到一类人, 第一次见他觉得太傲了, 接触了一段时间

发现这人了不起, 后面了解多了, 还是很喜欢和他交朋友. 人不错.

  人是最复杂的,也是最容易改变的.关键需要多了解.

前言

   到这里逐渐切入正题了, 当一个构想 投入生产环境一般 需要下面几个步骤.

1算法/思路 构思

2算法实现 测试

3.封装基础算法结构库

4.算法/思路结构库 测试

5. 投入生产环境轻微重构

6.生产环境测试

7.实战检测.

  所以封装一个库还是有些流程比较耗时的. 我们这里分享的是关于一个二叉树基础库的分享. 原先花了2天使用红黑树实现,

但是最后磕磕碰碰,抄抄补补搞出来但是代码很不好维护,最后退而求其次采用 二叉查找树构造了一个基础库. 并测试了一下基本可以.

等下一次直接用到实战环境中.

  首选学习这个二叉树 库封装 需要

    1.了解二叉树基础原理

    2.了解C接口的简单设计

  能够学到

    1.C接口设计的一些技巧

    2.接口简单测试

首先看下面接口文档 tree.h

  1. #ifndef _H_TREE
  2. #define _H_TREE
  3.  
  4. //4.0 控制台打印错误信息, fmt必须是双引号括起来的宏
  5. #ifndef CERR
  6. #define CERR(fmt, ...) \
  7. fprintf(stderr,"[%s:%s:%d][error %d:%s]" fmt "\r\n",\
  8. __FILE__, __func__, __LINE__, errno, strerror(errno),##__VA_ARGS__)
  9. #endif/* !CERR */
  10.  
  11. //4.1 控制台打印错误信息并退出, t同样fmt必须是 ""括起来的字符串常量
  12. #ifndef CERR_EXIT
  13. #define CERR_EXIT(fmt,...) \
  14. CERR(fmt,##__VA_ARGS__),exit(EXIT_FAILURE)
  15. #endif/* !ERR */
  16.  
  17. /*
  18. * 这里是简单二叉查找树封装的基库,封装库的库
  19. * 需要用的的一些辅助结构,主要是通用结构和申请释放的函数指针
  20. */
  21. typedef struct tree* tree_t;
  22. typedef void* (*pnew_f)();
  23. typedef void (*vdel_f)(void* node);
  24. typedef int (*icmp_f)(void* ln, void* rn);
  25.  
  26. // __开头一般意思是不希望你使用,私有的,系统使用
  27. struct __tnode {
  28. struct __tnode* lc;
  29. struct __tnode* rc;
  30. };
  31. /*
  32. * 这个宏必须放在使用的结构体开头,如下
  33. * struct persion {
  34. _TREE_HEAD;
  35. char* name;
  36. int age;
  37. ...
  38. * }
  39. *
  40. */
  41. #define _TREE_HEAD \
  42. struct __tnode __tn
  43.  
  44. /*
  45. * new : 结点申请内存用的函数指针, 对映参数中是 特定结构体指针
  46. * acmp : 用于添加比较
  47. * gdcmp : 两个结点比较函数,用户查找和删除
  48. * del : 结点回收函数,第一个参数就是 二叉树中保存的结点地址
  49. * ret : 返回创建好的二叉树结构, 这里是 tree_t 结构
  50. */
  51. tree_t tree_create(pnew_f new, icmp_f acmp, icmp_f gdcmp, vdel_f del);
  52.  
  53. /*
  54. * proot : 指向tree_t 根结点的指针,
  55. * node : 待处理的结点对象, 会调用new(node) 创建新结点
  56. * ret : proot 即是输入参数也是返回参数,返回根结点返回状况
  57. */
  58. void tree_add(tree_t* proot, void* node);
  59.  
  60. /*
  61. * proot : 输入和输出参数,指向根结点的指针
  62. * node : 删除结点,这里会调用 cmp(node 左参数, foreach) 找见,通过del(find) 删除
  63. */
  64. void tree_del(tree_t* proot, void* node);
  65.  
  66. /*
  67. * root : 根结点,查找的总对象
  68. * node : 查找条件,会通过cmp(node, foreach)去查找
  69. * parent : 返回查找到的父亲结点
  70. * ret : 返回查找到的结点对象
  71. */
  72. void* tree_get(tree_t root, void* node, void** parent);
  73.  
  74. /*
  75. * proot : 指向二叉树数结点指针
  76. * 会调用 del(foreach) 去删除所有结点,并将所有还原到NULL
  77. */
  78. void tree_destroy(tree_t* proot);
  79.  
  80. #endif // !_H_TREE

上面有些代码在实战环节是要去掉和统一修改的.这里是为了降低耦合性,方便测试,就放在一起了.

接口比较精简,还可以更精简,下次再优化.应该一看都明白上面代码是干什么的. 需要注意的是

在你想使用二叉树性质的 结构体中 需要在第一个 成员位置 加入

_TREE_NODE;

举例如下

  1. //通用结构体变量
  2. struct dict {
  3. _TREE_HEAD;
  4. char* key;
  5. char* value;
  6. };

至于为什么,想一想也都明白了,这样的代码或者说技巧 太多了, Linux内核中结构喜欢 将其放在最末的位置,会有一个

typeof 宏 判断位置.那下面我们开始说说具体设计. 扯一点,一个需要C入门选手,要么把C语言之父的书看一遍,倒着看一遍.

写一遍或理解会用上面的结构体设计,基本C这块语法都明白了.

  一定要多写代码, 因为未来不清楚, 但可以知道的是不好好写代码, 那现在都不清楚了. 大家觉得呢.

正文

1.说细节实现

  首先看创建函数定义,这里主要用到函数指针技巧,比较直白.

  1. //内部使用的主要结构
  2. struct tree {
  3. //保存二叉树的头结点
  4. struct __tnode* root;
  5.  
  6. //构建,释放,删除操作的函数指针
  7. pnew_f new;
  8. icmp_f acmp;
  9. icmp_f gdcmp;
  10. vdel_f del;
  11. };
  12.  
  13. /*
  14. * new : 结点申请内存用的函数指针, 对映参数中是 特定结构体指针
  15. * acmp : 用于添加比较
  16. * gdcmp : 两个结点比较函数,用户查找和删除
  17. * del : 结点回收函数,第一个参数就是 二叉树中保存的结点地址
  18. * ret : 返回创建好的二叉树结构, 这里是 tree_t 结构
  19. */
  20. tree_t
  21. tree_create(pnew_f new, icmp_f acmp, icmp_f gdcmp, vdel_f del)
  22. {
  23. tree_t root = malloc(sizeof(struct tree));
  24. if (NULL == root)
  25. CERR_EXIT("malloc struct tree error!");
  26.  
  27. //初始化挨个操作
  28. memset(root, , sizeof(struct tree));
  29. root->new = new;
  30. root->acmp = acmp;
  31. root->gdcmp = gdcmp;
  32. root->del = del;
  33.  
  34. return root;
  35. }

上面主要是需要注册4个函数, 第一个new自然是分配内存的操作返回void*就是构造好的内存, acmp是添加结点的时候比较函数,

gdcmp 是 get 和 del 时候需要调用的查找函数指针, 对于del可以没有这个时候,可以传入NULL,表示不需要帮忙回收内存.

大家可以仔细考虑一下为什么要这些.

首先创建和销毁是必须的,后面 add的时候添加的是 node 结点, 而查找的时候是比较的是 关键字key结构是不一样的.

同样看一下回收函数

  1. static void __tree_destroy(struct __tnode* root, vdel_f del)
  2. {
  3. if (root) {
  4. __tree_destroy(root->lc, del);
  5. __tree_destroy(root->rc, del);
  6. del(root); //结点删除采用注册方法
  7. }
  8. }
  9.  
  10. /*
  11. * proot : 指向二叉树数结点指针
  12. * 会调用 del(foreach) 去删除所有结点,并将所有还原到NULL
  13. */
  14. void
  15. tree_destroy(tree_t* proot)
  16. {
  17. tree_t root;
  18. if ((!proot) || !(root = *proot))
  19. return;
  20. if (root->root && root->del)
  21. __tree_destroy(root->root, root->del);
  22. free(*proot); //单独释放最外层内容
  23. *proot = NULL;
  24. }

比较质朴没有好解释的,最后会让释放的指针指向NULL.

后面就是二叉查找树插入查找和删除算法实现了,比较基础,对着书翻译就可以了.添加代码如下

  1. /*
  2. * proot : 指向tree_t 根结点的指针,
  3. * node : 待处理的结点对象, 会调用new(node) 创建新结点
  4. * ret : proot 即是输入参数也是返回参数,返回根结点返回状况
  5. */
  6. void
  7. tree_add(tree_t* proot, void* node)
  8. {
  9. tree_t tm;
  10. struct __tnode *n, *p = NULL;
  11. icmp_f cmp;
  12. int tmp = ;
  13.  
  14. if ((!proot) || (!node) || !(tm = *proot)) //参数无效直接返回
  15. return;
  16. if (!(n = tm->root)) { //插入的结点为头结点,直接赋值返回
  17. tm->root = tm->new(node);
  18. return;
  19. }
  20. //下面开始找 待插入结点
  21. cmp = tm->acmp;
  22. while (n) {
  23. if ((tmp = cmp(node, n)) == ) //这种情况是不允许插入的
  24. return;
  25. p = n;
  26. if (tmp < )
  27. n = n->lc;
  28. else
  29. n = n->rc;
  30. }
  31.  
  32. //找见了开始插入结点
  33. if (tmp < )
  34. p->lc = tm->new(node);
  35. else
  36. p->rc = tm->new(node);
  37. }

对于cmp

  1. typedef int (*icmp_f)(void* ln, void* rn);

这里有点约定, ln == rn 返回0, ln>rn 返回 >0 反之返回<0. 其中 传入的基参数 .都是做第一个参数.

下一版本想改为

  1. //int cmp(void* node, void* rn); 必须是这样格式
  2. typedef int (*icmp_f)();

弱约束,可以用.毕竟底层库应该灵活,上层库应该写死. 这样在难处学习成本高,简单处学习成本低. 等同于红黑树的添加和查找.

后面还有一个删除代码

  1. /*
  2. * proot : 输入和输出参数,指向根结点的指针
  3. * node : 删除结点,这里会调用 cmp(node 左参数, foreach) 找见,通过del(find) 删除
  4. */
  5. void
  6. tree_del(tree_t* proot, void* node)
  7. {
  8. tree_t tm;
  9. struct __tnode *n, *p, *t, *tp;
  10. if ((!proot) || (!node) || !(tm = *proot) || !(tm->root))
  11. return;
  12. //查找一下这个结点,如果不存在直接返回
  13. if (!(n = tree_get(tm, node, (void**)&p)))
  14. return;
  15. //第一种删除和操作
  16. if ((!n->lc || !n->rc) && !(t = n->lc))
  17. t = n->rc;
  18. else { //第二种情况,将右子树最小结点和当前删除结点交换
  19. for (tp = n, t = tp->rc; (t->lc); tp = t, t = t->lc)
  20. ; //找见了最小的左子树结点n 和父结点p
  21. if (tp->lc == t)
  22. tp->lc = t->rc;
  23. else
  24. tp->rc = t->rc;
  25. //移动孩子关系
  26. t->lc = n->lc;
  27. t->rc = n->rc;
  28. }
  29.  
  30. if (!p) //设置新的root结点
  31. tm->root = t;
  32. else {
  33. if (p->lc == n) //调整父亲和孩子关系,需要你理解二叉查找树,否则那就相信我吧
  34. p->lc = t;
  35. else
  36. p->rc = t;
  37. }
  38. //这里释放那个结点
  39. if (tm->del)
  40. tm->del(n);
  41. }

删除思路解释,单节点删除,父节点指向后继, 多结点找到右子树中最小的结点当做新结点,再删除它.上一个版本用尾递归,这里采用的是非递归实现.

对于查找是这样的,也会一起找到父节点

  1. /*
  2. * root : 根结点,查找的总对象
  3. * node : 查找条件,会通过cmp(node, foreach)去查找
  4. * parent : 返回查找到的父亲结点
  5. * ret : 返回查找到的结点对象
  6. */
  7. void*
  8. tree_get(tree_t root, void* node, void** parent)
  9. {
  10. struct __tnode *n, *p = NULL;
  11. icmp_f cmp;
  12. int tmp;
  13.  
  14. if(parent) //初始化功能
  15. *parent = NULL;
  16. if ((!node) || (!root) || !(n = root->root))
  17. return NULL;
  18. //查找结点
  19. cmp = root->gdcmp;
  20. while (n) {
  21. if ((tmp = cmp(node, n)) == ){ //这种情况是不允许插入的
  22. //返回父亲结点,没有就置空
  23. if (parent)
  24. *parent = p;
  25. break;
  26. }
  27. p = n;
  28. if (tmp < )
  29. n = n->lc;
  30. else
  31. n = n->rc;
  32. }
  33.  
  34. return n;
  35. }

特别是开头的

  1. if(parent) //初始化功能
  2. *parent = NULL;

为了是查找返回数据都是正常数据,没有意外.

到这里基本上二叉树基库就整理完毕了. 主要是一些C接口设计的技巧 + 二叉树查找树的简单算法.

还是比较直白的.下一个版本 将公有头文件内容移除去,会更简约一点.

2.tree.c 代码完整展示

  完整代码展示如下

  1. #include "tree.h"
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <errno.h>
  5. #include <string.h>
  6.  
  7. //内部使用的主要结构
  8. struct tree {
  9. //保存二叉树的头结点
  10. struct __tnode* root;
  11.  
  12. //构建,释放,删除操作的函数指针
  13. pnew_f new;
  14. icmp_f acmp;
  15. icmp_f gdcmp;
  16. vdel_f del;
  17. };
  18.  
  19. /*
  20. * new : 结点申请内存用的函数指针, 对映参数中是 特定结构体指针
  21. * acmp : 用于添加比较
  22. * gdcmp : 两个结点比较函数,用户查找和删除
  23. * del : 结点回收函数,第一个参数就是 二叉树中保存的结点地址
  24. * ret : 返回创建好的二叉树结构, 这里是 tree_t 结构
  25. */
  26. tree_t
  27. tree_create(pnew_f new, icmp_f acmp, icmp_f gdcmp, vdel_f del)
  28. {
  29. tree_t root = malloc(sizeof(struct tree));
  30. if (NULL == root)
  31. CERR_EXIT("malloc struct tree error!");
  32.  
  33. //初始化挨个操作
  34. memset(root, , sizeof(struct tree));
  35. root->new = new;
  36. root->acmp = acmp;
  37. root->gdcmp = gdcmp;
  38. root->del = del;
  39.  
  40. return root;
  41. }
  42.  
  43. /*
  44. * proot : 指向tree_t 根结点的指针,
  45. * node : 待处理的结点对象, 会调用new(node) 创建新结点
  46. * ret : proot 即是输入参数也是返回参数,返回根结点返回状况
  47. */
  48. void
  49. tree_add(tree_t* proot, void* node)
  50. {
  51. tree_t tm;
  52. struct __tnode *n, *p = NULL;
  53. icmp_f cmp;
  54. int tmp = ;
  55.  
  56. if ((!proot) || (!node) || !(tm = *proot)) //参数无效直接返回
  57. return;
  58. if (!(n = tm->root)) { //插入的结点为头结点,直接赋值返回
  59. tm->root = tm->new(node);
  60. return;
  61. }
  62. //下面开始找 待插入结点
  63. cmp = tm->acmp;
  64. while (n) {
  65. if ((tmp = cmp(node, n)) == ) //这种情况是不允许插入的
  66. return;
  67. p = n;
  68. if (tmp < )
  69. n = n->lc;
  70. else
  71. n = n->rc;
  72. }
  73.  
  74. //找见了开始插入结点
  75. if (tmp < )
  76. p->lc = tm->new(node);
  77. else
  78. p->rc = tm->new(node);
  79. }
  80.  
  81. /*
  82. * proot : 输入和输出参数,指向根结点的指针
  83. * node : 删除结点,这里会调用 cmp(node 左参数, foreach) 找见,通过del(find) 删除
  84. */
  85. void
  86. tree_del(tree_t* proot, void* node)
  87. {
  88. tree_t tm;
  89. struct __tnode *n, *p, *t, *tp;
  90. if ((!proot) || (!node) || !(tm = *proot) || !(tm->root))
  91. return;
  92. //查找一下这个结点,如果不存在直接返回
  93. if (!(n = tree_get(tm, node, (void**)&p)))
  94. return;
  95. //第一种删除和操作
  96. if ((!n->lc || !n->rc) && !(t = n->lc))
  97. t = n->rc;
  98. else { //第二种情况,将右子树最小结点和当前删除结点交换
  99. for (tp = n, t = tp->rc; (t->lc); tp = t, t = t->lc)
  100. ; //找见了最小的左子树结点n 和父结点p
  101. if (tp->lc == t)
  102. tp->lc = t->rc;
  103. else
  104. tp->rc = t->rc;
  105. //移动孩子关系
  106. t->lc = n->lc;
  107. t->rc = n->rc;
  108. }
  109.  
  110. if (!p) //设置新的root结点
  111. tm->root = t;
  112. else {
  113. if (p->lc == n) //调整父亲和孩子关系,需要你理解二叉查找树,否则那就相信我吧
  114. p->lc = t;
  115. else
  116. p->rc = t;
  117. }
  118. //这里释放那个结点
  119. if (tm->del)
  120. tm->del(n);
  121. }
  122.  
  123. /*
  124. * root : 根结点,查找的总对象
  125. * node : 查找条件,会通过cmp(node, foreach)去查找
  126. * parent : 返回查找到的父亲结点
  127. * ret : 返回查找到的结点对象
  128. */
  129. void*
  130. tree_get(tree_t root, void* node, void** parent)
  131. {
  132. struct __tnode *n, *p = NULL;
  133. icmp_f cmp;
  134. int tmp;
  135.  
  136. if(parent) //初始化功能
  137. *parent = NULL;
  138. if ((!node) || (!root) || !(n = root->root))
  139. return NULL;
  140. //查找结点
  141. cmp = root->gdcmp;
  142. while (n) {
  143. if ((tmp = cmp(node, n)) == ){ //这种情况是不允许插入的
  144. //返回父亲结点,没有就置空
  145. if (parent)
  146. *parent = p;
  147. break;
  148. }
  149. p = n;
  150. if (tmp < )
  151. n = n->lc;
  152. else
  153. n = n->rc;
  154. }
  155.  
  156. return n;
  157. }
  158.  
  159. //实际的删除函数,采用后续删除
  160. static void __tree_destroy(struct __tnode* root, vdel_f del)
  161. {
  162. if (root) {
  163. __tree_destroy(root->lc, del);
  164. __tree_destroy(root->rc, del);
  165. del(root); //结点删除采用注册方法
  166. }
  167. }
  168.  
  169. /*
  170. * proot : 指向二叉树数结点指针
  171. * 会调用 del(foreach) 去删除所有结点,并将所有还原到NULL
  172. */
  173. void
  174. tree_destroy(tree_t* proot)
  175. {
  176. tree_t root;
  177. if ((!proot) || !(root = *proot))
  178. return;
  179. if (root->root && root->del)
  180. __tree_destroy(root->root, root->del);
  181. free(*proot); //单独释放最外层内容
  182. *proot = NULL;
  183. }

总长度还是比较短的.上面代码写了几遍,都没有加测试接口. 后面单独写测试demo.因为是封装库的库,测试代码会多一点.

3.说测试结果

  到这里就是说测试的时候,先简单看一个test.c 测试,编译命令是

  1. gcc -g -Wall -o test.out test.c tree.c

源码如下

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include "tree.h"
  5.  
  6. //通用结构体变量
  7. struct dict {
  8. _TREE_HEAD;
  9. char* key;
  10. char* value;
  11. };
  12.  
  13. static void* __dict_new(void* arg)
  14. {
  15. return arg;
  16. }
  17.  
  18. //为了通用库,这种比较算法比较不好,采用hash不能够唯一确定
  19. static int __dict_acmp(struct dict* ln, struct dict* rn)
  20. {
  21. return strcmp(ln->key, rn->key);
  22. }
  23. static int __dict_gdcmp(const char* ln, struct dict* rn)
  24. {
  25. return strcmp(ln, rn->key);
  26. }
  27.  
  28. /*
  29. * 这里测试 tree.c 基类型测试的
  30. */
  31. int main(int argc, char* argv[])
  32. {
  33. struct dict *pd , *pp;
  34. struct dict dt1 = { { , }, "", "" };
  35. struct dict dt2 = { { , }, "","" };
  36. struct dict dt3 = { { , }, "","" };
  37. struct dict dt4 = { { , }, "", "" };
  38. struct dict dt5 = { { , }, "","" };
  39.  
  40. //创建一个结点,后面创建删除
  41. tree_t root = tree_create(__dict_new, (icmp_f)__dict_acmp, (icmp_f)__dict_gdcmp, NULL);
  42.  
  43. //开始添加结点
  44. tree_add(&root, &dt1);
  45. tree_add(&root, &dt2);
  46. tree_add(&root, &dt3);
  47. tree_add(&root, &dt4);
  48. tree_add(&root, &dt5);
  49.  
  50. //得到这个结点,并返回
  51. pd = tree_get(root, "", NULL);
  52. printf("key:[%s], value:[%s].\n", pd->key, pd->value);
  53.  
  54. pd = tree_get(root, "", (void**)&pp);
  55. printf("key:[%s], value:[%s].\n", pd->key, pd->value);
  56. printf("key:[%s], value:[%s].\n", pp->key, pp->value);
  57.  
  58. //删除结点测试,这个普通树型结构确实不好
  59. tree_del(&root, "");
  60. pd = tree_get(root, "", (void**)&pp);
  61. printf("key:[%s], value:[%s].\n", pd->key, pd->value);
  62. if (!pp)
  63. puts("应该不存在的!");
  64.  
  65. //通过单点调试,内存检测一切正常
  66. tree_destroy(&root);
  67.  
  68. system("pause");
  69. return ;
  70. }

测试结果,原先是在window上,后面在Linux上测试了.结果如下

一切正常.

第二个测试,测试在堆上分配是否正常 main.c

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <errno.h>
  5. #include "tree.h"
  6.  
  7. //继续测试堆上分配
  8. struct node {
  9. _TREE_HEAD;
  10. char* key;
  11. char* value;
  12. };
  13.  
  14. //构建运用到的函数
  15. static void* __node_new(struct node* n)
  16. {
  17. struct node* nn = calloc(, sizeof(struct node));
  18. if(NULL == nn)
  19. CERR_EXIT("malloc struct node error!");
  20. nn->key = n->key;
  21. nn->value = n->value;
  22. //返回最终结果
  23. return nn;
  24. }
  25.  
  26. //添加时候查找函数
  27. static int __node_acmp(void* ln, void* rn)
  28. {
  29. return strcmp(((struct node*)ln)->key, ((struct node*)rn)->key);
  30. }
  31.  
  32. //查找和删除的查找函数
  33. static int __node_gdcmp(void* ln, void* rn)
  34. {
  35. return strcmp(ln, ((struct node*)rn)->key);
  36. }
  37.  
  38. //简单测试函数
  39. static void __node_puts(void* arg)
  40. {
  41. struct node* n = arg;
  42. if(NULL == n)
  43. puts("now node is empty!");
  44. else
  45. printf("key:%s, value:%s.\n", n->key, n->value);
  46. }
  47.  
  48. //简单释放函数
  49. static void __node_delete(void* arg)
  50. {
  51. __node_puts(arg);
  52. free(arg);
  53. }
  54.  
  55. //写到这里自己都想抱怨一句前戏太长了, tree.c 其实本质是个通用算法库,...
  56.  
  57. /*
  58. * 这里继续测试一下 tree 基类库接口
  59. */
  60. int main(int argc, char* argv[])
  61. {
  62. tree_t root = tree_create(__node_new, __node_acmp, __node_gdcmp, __node_delete);
  63.  
  64. //这里就添加结点
  65. struct node ntmp = { {NULL, NULL}, "a", ""};
  66. tree_add(&root, &ntmp);
  67.  
  68. ntmp.key = "bb";
  69. ntmp.value = "ccccccc";
  70. tree_add(&root, &ntmp);
  71.  
  72. ntmp.key = "bbc";
  73. ntmp.value = "ccccccc";
  74. tree_add(&root, &ntmp);
  75.  
  76. ntmp.key = "bbcc";
  77. ntmp.value = "ccccccc";
  78. tree_add(&root, &ntmp);
  79.  
  80. ntmp.key = "bbcccc";
  81. ntmp.value = "dd你好ccc";
  82. tree_add(&root, &ntmp);
  83. //tree_destroy(&root);
  84.  
  85. if(NULL == root)
  86. puts("root is null");
  87. ntmp.key = "好的";
  88. ntmp.value = "cccok就这样c";
  89. tree_add(&root, &ntmp);
  90.  
  91. //这里查找结点
  92. void *p, *n;
  93. n = tree_get(root, "好的", &p);
  94. if(p)
  95. __node_puts(p);
  96. else
  97. puts("没有父结点");
  98. __node_puts(n);
  99.  
  100. //删除结点
  101. tree_del(&root, "好的");
  102.  
  103. tree_destroy(&root);
  104.  
  105. return ;
  106. }

编译命令,Makefile文件内容如下

  1. main.out:main.c tree.c
  2. gcc -g -Wall -o $@ $^

运行结果截图如下

一切正常没有内存泄露.

后面准备到库再进行生产测试.

后记

  这里,这个基础tree C库基本封装了,根据库简单修改一下基本就可以用在开发中了.下一个版本利用这个库 构造一个 C 配置文件读取接口.

让框架具备简单配置文件热读取的能力.扯一点,像这些解析配置的引擎难点都在 语法解析上.其它都好搞.以后有机会带大家手把手写json,csv 解析'引擎'.

这里就这样了. 错误是难免的, 因为经历的太少, 拜~.

C 封装一个简单二叉树基库的更多相关文章

  1. Directx11学习笔记【四】 封装一个简单的Dx11DemoBase

    根据前面两个笔记的内容,我们来封装一个简单的基类,方便以后的使用. 代码和前面类似,没有什么新的内容,直接看代码吧(由于代码上次都注释了,这次代码就没怎么写注释o(╯□╰)o) Dx11DemoBas ...

  2. 网络游戏开发-服务器(01)Asp.Net Core中的websocket,并封装一个简单的中间件

    先拉开MSDN的文档,大致读一遍 (https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/websockets) WebSocket 是一 ...

  3. 代码改变世界 | 如何封装一个简单的 Koa

    下面给大家带来:封装一个简单的 Koa Koa 是基于 Node.js 平台的下一代 web 开发框架 Koa 是一个新的 web 框架,可以快速而愉快地编写服务端应用程序,本文将跟大家一起学习:封装 ...

  4. python+selenium之自定义封装一个简单的Log类

    python+selenium之自定义封装一个简单的Log类 一. 问题分析: 我们需要封装一个简单的日志类,主要有以下内容: 1. 生成的日志文件格式是 年月日时分秒.log 2. 生成的xxx.l ...

  5. Python之自定义封装一个简单的Log类

    参考:http://www.jb51.net/article/42626.htm 参考:http://blog.csdn.net/u011541946/article/details/70198676 ...

  6. Python+Selenium中级篇之8-Python自定义封装一个简单的Log类《转载》

    Python+Selenium中级篇之8-Python自定义封装一个简单的Log类: https://blog.csdn.net/u011541946/article/details/70198676

  7. C 封装一个通用链表 和 一个简单字符串开发库

    引言 这里需要分享的是一个 简单字符串库和 链表的基库,代码也许用到特定技巧.有时候回想一下, 如果我读书的时候有人告诉我这些关于C开发的积淀, 那么会走的多直啊.刚参加工作的时候做桌面开发, 服务是 ...

  8. C 构造一个 简单配置文件读取库

    前言 最近看到这篇文章, json引擎性能对比报告 http://www.oschina.net/news/61942/cpp-json-compare?utm_source=tuicool 感觉技术 ...

  9. 仿照jquery封装一个自己的js库(一)

    所谓造轮子的好处就是复习知识点,加深对原版jquery的理解. 本文系笔者学习jquery的笔记,记述一个名为"dQuery"的初级版和缩水版jquery库的实现.主要涉及知识点包 ...

随机推荐

  1. python学习(三):matplotlib学习

    前言:matplotlib是一个python的第三方库,里面的pyplot可以用来作图.下面来学习一下如何使用它的资源. 一.使用前 首先在python中使用任何第三方库时,都必须先将其引入.即: i ...

  2. 关于 C/C++ 的文章

    关于 C/C++ 的文章,以前写的博客在百度,百度关了,只能一个一个复制了,百度太.....

  3. Linux-Apache+Mysql+PHP+PHPWind(重点Apache+PHP集成环境)

    整理Apache+Mysql+PHP+PHPWind(Apache+PHP集成环境) 一.情况简述: 1.虚拟机VM上面CentOS 2.全部yum安装(yum安装与源码安装的安装路径不同) 二.操作 ...

  4. CODESOFT 2015中的二维码该怎样生成

    由于二维条码具有储存量大.保密性高.追踪性高.抗损性强.备援性大.成本便宜等特性,其应用 渐趋广泛,因此二维码的制作对于CODESOFT条码设计软件的用户来讲可谓司空见惯.我们最常见的二维码要数QR码 ...

  5. 记录特殊情况的Python脚本的内存异常与处理

    问题 Python 脚本使用 requests 模块做 HTTP 请求,验证代理 IP 的可用性,速度等. 设定 HTTP 请求的 connect timeout 与 read response ti ...

  6. 学习总结 java连接数据库

    package com.hanqi.test; import java.sql.*; public class jdbcTest { public static void main(String[] ...

  7. PZISP自动下载软件运行时出现“应用程序无法启动,因为应用程序的并行配置不正确”

    在win7下以管理员身份运行“PZISP自动下载软件”时出现“应用程序无法启动,因为应用程序的并行配置不正确”时,是因为系统里面没有一些visual c++库 想一想,反正以后也要用上VS2010的, ...

  8. 10 Code Coverage Tools for C & C++

    Code coverage is a measure used in software testing that describes the degree to which the source co ...

  9. php中使用end方法报错

    <b>Strict Standards</b>:  Only variables should be passed by reference in <b> 1.如果 ...

  10. iOS 获取当前城市

    1.倒入头文件 #import <CoreLocation/CoreLocation.h> 2.实现定位协议CLLocationManagerDelegate 3.定义定位属性  @pro ...