linux内核二叉查找树的应用

原创 2015年11月18日 09:45:43

参考资料:

1. **百科
3. 下面这个连接也是介绍二叉树搜索的,有空可以参考一下。

什么叫二叉查找树
二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树


man-page 部分翻译

SYNOPSIS

#include <search.h>
void *tsearch(const void *key, void **rootp,int (*compar)(const void *, const void *));
void *tfind(const void *key, void *const *rootp,int (*compar)(const void *, const void *));
void *tdelete(const void *key, void **rootp,int (*compar)(const void *, const void *));
void twalk(const void *root, void (*action)(const void *nodep,const VISIT which,const int depth));
#define _GNU_SOURCE         /* See feature_test_macros(7) */
       #include <search.h>
void tdestroy(void *root, void (*free_node)(void *nodep));

DESCRIPTION

        tsearch(), tfind(), twalk(), and tdelete() manage a binary tree.
       They are generalized from Knuth (6.2.2) Algorithm T.  The first field
       in each node of the tree is a pointer to the corresponding data item.
树的每个节点的第一个域都是指针,该指针指向相对应的数据项。
       (The calling program must store the actual data.)  compar points to a
(这些函数的调用者一定要存储数据项的实际内存)
       comparison routine, which takes pointers to two items.  It should
compar参数指向一个函数指针,该函数指针有两个参数,分别指向两个数据项。
return an integer which is negative, zero, or positive, depending on
该函数指针应当返回一个整型数如负数、0、正数,而这具体是多少则完全取决于它的第一个数据项是小于、等于、大于第二个数据项。
       whether the first item is less than, equal to, or greater than the
       second.

       tsearch() searches the tree for an item.  key points to the item to
tsearch()用于在一颗指定的树中搜索一个数据项,而参数key指针就指向这个被搜索的数据项,
       be searched for.  rootp points to a variable which points to the root
rootp指针指向这棵树的根部。
       of the tree.  If the tree is empty, then the variable that rootp
如果这棵树是空的,那么rootp指针所指变量应当被设置为NULL。
       points to should be set to NULL.  If the item is found in the tree,
如果这个待搜索的数据项在树中被找到了,那么tsearch()将会返回一个指向该元素的指针。
       then tsearch() returns a pointer to it.  If it is not found, then
如果没有找到,tsearch()将会把它添加到树中去,然后返回一个指向这个新添加的数据项的指针。
       tsearch() adds it, and returns a pointer to the newly added item.

       tfind() is like tsearch(), except that if the item is not found, then
       tfind() returns NULL.

       tdelete() deletes an item from the tree.  Its arguments are the same
       as for tsearch().

       twalk() performs depth-first, left-to-right traversal of a binary
twalk()按照深度优先、从左到右的顺序编译一颗二叉树(其实就是中序遍历二叉树).
       tree.  root points to the starting node for the traversal.  If that
       node is not the root, then only part of the tree will be visited.
       twalk() calls the user function action each time a node is visited
       (that is, three times for an internal node, and once for a leaf).
       action, in turn, takes three arguments.  The first argument is a
       pointer to the node being visited.  The structure of the node is
       unspecified, but it is possible to cast the pointer to a pointer-to-
       pointer-to-element in order to access the element stored within the
       node.  The application must not modify the structure pointed to by
       this argument.  The second argument is an integer which takes one of
       the values preorder, postorder, or endorder depending on whether this
       is the first, second, or third visit to the internal node, or the
       value leaf if this is the single visit to a leaf node.  (These
       symbols are defined in <search.h>.)  The third argument is the depth
       of the node; the root node has depth zero.

       (More commonly, preorder, postorder, and endorder are known as
       preorder, inorder, and postorder: before visiting the children, after
       the first and before the second, and after visiting the children.
       Thus, the choice of name postorder is rather confusing.)

       tdestroy() removes the whole tree pointed to by root, freeing all
       resources allocated by the tsearch() function.  For the data in each
       tree node the function free_node is called.  The pointer to the data
       is passed as the argument to the function.  If no such work is
       necessary, free_node must point to a function doing nothing.


代码示例

// 这里的代码是以man-page给的示例代码为参考的。
#define _GNU_SOURCE     /* Expose declaration of tdestroy() */ 
#include <search.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>

static void *root = NULL;

static void * xmalloc(unsigned n)
{
        void *p;
        p = malloc(n);
        if (p)
                return p;
        fprintf(stderr, "insufficient memory\n");
        exit(EXIT_FAILURE);
}

static int compare(const void *pa, const void *pb)
{
        printf("compare, *(int *)pa = %d, *(int *)pb = %d\n", *(int *)pa, *(int *)pb);
        if (*(int *) pa < *(int *) pb)
                return -1;
        if (*(int *) pa > *(int *) pb)
                return 1;
        return 0;
}

static void action(const void *nodep, const VISIT which, const int depth)
{
        int *datap;
        //printf("which = %d, preorder = %d, postorder = %d, endorder = %d, leaf = %d\n", which, preorder, postorder, endorder, leaf);
        switch (which)
        {
                case preorder:
                        //datap = *(int **) nodep;
                        //printf("%6d\n", *datap);
                        break;
                case postorder:
                        datap = *(int **) nodep;
                        printf("%6d\n", *datap);
                        break;
                case endorder:
                        //datap = *(int **) nodep;
                        //printf("%6d\n", *datap);
                        break;
                case leaf:
                        datap = *(int **) nodep;
                        printf("%6d\n", *datap);
                        break;
        }
}

// 本来函数fun()中的所有内容是直接放在main函数中的,而这里将其单独拿出来就是为了说明tsearch的第一个参数的内存问题。
void fun(void)
{
        int i, *ptr;
        void *val;
        //srand(time(NULL)); // 官方文档上的例子是采用随机数,但是由于每次执行都会产生不同的随机数,这样不便于观察,所以这里采用下面固定的数组。
        int array[12] = {2, 2, 1, 12, 9, 4, 8, 10, 3, 7, 5, 11};
        for (i = 0; i < 12; i++)
        {
                printf("~~~~~~~~~~~~ i = %d ~~~~~~~~~~~~~~\n", i);
                // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                ptr = xmalloc(sizeof(int));
                //*ptr = rand() & 0xff; // 取随机数的低8位。
                *ptr = array[i];
                // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~

                // ptr 所指内存最好是实际存在的,而不是临时的(如栈空间),
                // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                // ptr = array + i;
                // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~

// root 初始状态应被设置成 NULL,它将始终指向一颗二叉树的根部。
// tsearch() 会把ptr所指内存的值与root树中各个节点(由于root树是一颗二叉排序树,所以实际操作并不是每个节点都需要比较的)的值按照compare()方法作比较,如果compare()返回0,则将ptr丢弃(即不将其存入root树);如果compare()返回1,则将其存入root树的右子树;如果compare()返回-1,则将其存入root树的左子树。
// compare()方法通常是按照strcmp(char *s1, char *s2)的思想来操作,即s1小于s2时,返回-1;s1等于s2时,返回0;s1大于s2时,返回1。
// 但是第一次调用tsearch() 时,由于root=NULL,tsearch() 会直接将ptr所指内存地址添加到root树中去,而不会去调用compare()函数了。
// tsearch() 是将ptr所指内存地址赋给root树子节点,而不是将ptr所指内存中的值拷贝到root树的子节点。单独写一个fun()函数就是为了验证这一点。                
                val = tsearch((void *) ptr, &root, compare);

                int *pint = *(int **)val;
                printf("ptr = %p, *ptr = %d, val = %p, pint = %p, *pint = %d, root = %p\n", ptr, *ptr, val, pint, *pint, root);
                if (val == NULL) // (me) tsearch() 中申请内存失败
                {
                        printf("tsearch return NULL, will exit\n");
                        exit(EXIT_FAILURE);
                }
                else if (pint != ptr) // ptr所指内存的值在root树中已经存在了,即pint所内存。
                {
                        printf("pint != ptr, will free(ptr)\n");
                        free(ptr);
                }
        }
}

int main(void)
{
        fun(); // fun()函数的内容本来是被直接写在main函数里的,而这里将其写到fun()函数里,是为了验证tsearch()的第一个参数最好是指向一个实际存在的内存。
        twalk(root, action);
        int key = 10;
        void *p = tfind((void *)&key, &root, compare);
        if(p)
        {
                int *pint = *(int **)p;
                printf("tfind key >>> p = %p, pint = %p, *pint = %d\n\n", p, pint, *pint);
        }

        p = tdelete((void *)&key, &root, compare);
        if(p)
        {
                int *pint = *(int **)p;
                printf("tdelete key, key's father is >>> p = %p, pint = %p, *pint = %d\n\n", p, pint, *pint);
        }
        twalk(root, action);

        tdestroy(root, free);
        exit(EXIT_SUCCESS);
}

编译/执行
$ gcc tree.c 
$ ./a.out 
~~~~~~~~~~~~ i = 0 ~~~~~~~~~~~~~~
ptr = 0x90e010, *ptr = 2, val = 0x90e030, pint = 0x90e010, *pint = 2, root = 0x90e030
~~~~~~~~~~~~ i = 1 ~~~~~~~~~~~~~~
compare, *(int *)pa = 2, *(int *)pb = 2
ptr = 0x90e060, *ptr = 2, val = 0x90e030, pint = 0x90e010, *pint = 2, root = 0x90e030
pint != ptr, will free(ptr)
~~~~~~~~~~~~ i = 2 ~~~~~~~~~~~~~~
compare, *(int *)pa = 1, *(int *)pb = 2
ptr = 0x90e060, *ptr = 1, val = 0x90e080, pint = 0x90e060, *pint = 1, root = 0x90e030
~~~~~~~~~~~~ i = 3 ~~~~~~~~~~~~~~
compare, *(int *)pa = 12, *(int *)pb = 2
ptr = 0x90e0b0, *ptr = 12, val = 0x90e0d0, pint = 0x90e0b0, *pint = 12, root = 0x90e030
~~~~~~~~~~~~ i = 4 ~~~~~~~~~~~~~~
compare, *(int *)pa = 9, *(int *)pb = 2
compare, *(int *)pa = 9, *(int *)pb = 12
ptr = 0x90e100, *ptr = 9, val = 0x90e120, pint = 0x90e100, *pint = 9, root = 0x90e030
~~~~~~~~~~~~ i = 5 ~~~~~~~~~~~~~~
compare, *(int *)pa = 4, *(int *)pb = 2
compare, *(int *)pa = 4, *(int *)pb = 12
compare, *(int *)pa = 4, *(int *)pb = 9
ptr = 0x90e150, *ptr = 4, val = 0x90e170, pint = 0x90e150, *pint = 4, root = 0x90e030
~~~~~~~~~~~~ i = 6 ~~~~~~~~~~~~~~
compare, *(int *)pa = 8, *(int *)pb = 2
compare, *(int *)pa = 8, *(int *)pb = 9
compare, *(int *)pa = 8, *(int *)pb = 4
ptr = 0x90e1a0, *ptr = 8, val = 0x90e1c0, pint = 0x90e1a0, *pint = 8, root = 0x90e030
~~~~~~~~~~~~ i = 7 ~~~~~~~~~~~~~~
compare, *(int *)pa = 10, *(int *)pb = 2
compare, *(int *)pa = 10, *(int *)pb = 9
compare, *(int *)pa = 10, *(int *)pb = 12
ptr = 0x90e1f0, *ptr = 10, val = 0x90e210, pint = 0x90e1f0, *pint = 10, root = 0x90e030
~~~~~~~~~~~~ i = 8 ~~~~~~~~~~~~~~
compare, *(int *)pa = 3, *(int *)pb = 2
compare, *(int *)pa = 3, *(int *)pb = 9
compare, *(int *)pa = 3, *(int *)pb = 4
ptr = 0x90e240, *ptr = 3, val = 0x90e260, pint = 0x90e240, *pint = 3, root = 0x90e030
~~~~~~~~~~~~ i = 9 ~~~~~~~~~~~~~~
compare, *(int *)pa = 7, *(int *)pb = 2
compare, *(int *)pa = 7, *(int *)pb = 9
compare, *(int *)pa = 7, *(int *)pb = 4
compare, *(int *)pa = 7, *(int *)pb = 9
compare, *(int *)pa = 7, *(int *)pb = 8
ptr = 0x90e290, *ptr = 7, val = 0x90e2b0, pint = 0x90e290, *pint = 7, root = 0x90e170
~~~~~~~~~~~~ i = 10 ~~~~~~~~~~~~~~
compare, *(int *)pa = 5, *(int *)pb = 4
compare, *(int *)pa = 5, *(int *)pb = 9
compare, *(int *)pa = 5, *(int *)pb = 8
compare, *(int *)pa = 5, *(int *)pb = 7
ptr = 0x90e2e0, *ptr = 5, val = 0x90e300, pint = 0x90e2e0, *pint = 5, root = 0x90e170
~~~~~~~~~~~~ i = 11 ~~~~~~~~~~~~~~
compare, *(int *)pa = 11, *(int *)pb = 4
compare, *(int *)pa = 11, *(int *)pb = 9
compare, *(int *)pa = 11, *(int *)pb = 12
compare, *(int *)pa = 11, *(int *)pb = 10
ptr = 0x90e330, *ptr = 11, val = 0x90e350, pint = 0x90e330, *pint = 11, root = 0x90e170
     1
     2
     3
     4
     5
     7
     8
     9
    10
    11
    12
compare, *(int *)pa = 10, *(int *)pb = 4
compare, *(int *)pa = 10, *(int *)pb = 9
compare, *(int *)pa = 10, *(int *)pb = 11
compare, *(int *)pa = 10, *(int *)pb = 10
tfind key >>> p = 0x1036230, pint = 0x1036210, *pint = 10

compare, *(int *)pa = 10, *(int *)pb = 4
compare, *(int *)pa = 10, *(int *)pb = 9
compare, *(int *)pa = 10, *(int *)pb = 11
compare, *(int *)pa = 10, *(int *)pb = 10
tdelete key, key's father is >>> p = 0x2311350, pint = 0x2311330, *pint = 11

     1
     2
     3
     4
     5
     7
     8
     9
    11
    12


思考1:
其他函数不变,只将compare()函数作如下修改(红色字体为修改部分),执行结果:twalk()遍历的结果是从大到小输出的。
static int compare(const void *pa, const void *pb)
{
        if (*(int *) pa < *(int *) pb)
                return 1;
        if (*(int *) pa > *(int *) pb)
                return -1;
        return 0;
}

思考2:
其他函数不变,只将compare()函数作如下修改(红色字体为修改部分),执行结果:twalk()遍历的结果是和root的第一个元素相同的元素。
static int compare(const void *pa, const void *pb)
{
        if (*(int *) pa < *(int *) pb)
                return 0;
        if (*(int *) pa > *(int *) pb)
                return 0;
        return 1// 此时返回1或者-1都是一样的。
}
分析:第一次调用 tsearch() 时,tsearch() 会将array的第一个元素2添加到root树,此时不会调用compare()函数;以后再执行 tsearch() 时,就会调用compare()函数了,由于此时只有当待搜索元素和root树中的元素相等时compare()才会返回非0值,所以只有和root树中相等的值才会被添加到root树中。

思考3:
如果fun()函数中为ptr指向的不是malloc出来的内存,而是局部变量array所指内存,那么twalk()的输出内容就会出错,而tdestroy(root, free)更是会导致进程崩溃。但是如果将fun()函数的内容抽出来直接放到main函数中,的twalk()便可正确输出了,但此时tdestroy(root, free)还是会崩溃,因为ptr所指内存是位于栈空间的局部变量,而不是malloc所申请的。

版权声明:本文为博主原创文章,未经博主允许不得转载。

python的简单实际应用(一)

python的简单实际应用(一) 初步需求:将一整个文件夹各个子目录中所有目标文件的所有对应元素的相同属性名称的不同字段全部取出写到excel表格以供下一步使用。 整体思路:(1)递归出所有目标...
  • java_thinkinger
  • java_thinkinger
  • 2015年11月02日 17:16
  • 1080

MyBatis简单应用

MyBatis的前身就是iBatis,iBatis本是apache的一个开源项目,2010年这个项目由apahce sofeware foundation 迁移到了google code,并且改名为M...
  • a468903507
  • a468903507
  • 2015年03月23日 23:53
  • 725

在首席架构师手里,应用架构如此设计

转载自:http://geek.csdn.net/news/detail/73332 无架构,不系统,架构是大型系统的关键。从形上看,架构是系统的骨架,支撑和链接各个部分;从神上看,架构是系统的...
  • boonya
  • boonya
  • 2016年05月11日 17:26
  • 5354

信息论的简单应用

转载地址:http://blog.sciencenet.cn/blog-677221-669159.html 我在帖子“大将军数学题2-答案”中,出了一道有关用老鼠检测毒药瓶的附加题:   有1...
  • zhongjishao
  • zhongjishao
  • 2013年04月10日 15:31
  • 1306

算法9-6:最大流的应用

最大流算法在现实生活中有着广泛的应用,从数据挖掘到图像处理,都有应用。现实生活中很多事物看起来是不相干的,而实际上只要通过数学建模,其实很多问题本质上都是一样的。 这里举的一些例子很...
  • caipeichao2
  • caipeichao2
  • 2014年06月27日 20:08
  • 1584

谈谈FFT到底有何用(吴臻志)--很宽泛的介绍FFT应用

谈谈FFT到底有何用 谨以此献给一直在致力于FFT算法芯片设计的同行们   FFT(快速傅里叶变换)是数字信号处理的超级经典算法,学过DSP或者芯片设计的人大多知道这个算法。但是,大家是否想过,...
  • w15778225
  • w15778225
  • 2014年05月03日 17:01
  • 3676

TensorFlow最新应用&资源集锦

TensorFlow最新应用&资源集锦
  • x_r_su
  • x_r_su
  • 2016年11月05日 11:25
  • 3546

Android 将自己的应用改为系统应用

所谓系统程序就是system/app目录中的程序,普通应用转换成系统程序后有稳定、减少内存(DATA)空间占用、恢复出厂设置后不会消失、修改系统时间、调用隐藏方法、系统关机重启、静默安装升级卸载应用等...
  • xx326664162
  • xx326664162
  • 2016年11月30日 16:19
  • 13058

单页web应用是什么?它又会给传统网站带来哪些好处?

什么是单页应用? 单页应用是指在浏览器中运行的应用,它们在使用期间不会重新加载页面。像所有的应用一样,它旨在帮助用户完成任务,比如“编写文档”或者“管理Web服务器”。可以认为单页应用是一种从We...
  • zuoninger
  • zuoninger
  • 2014年08月26日 09:06
  • 36089

链表的实际应用1

在上次的《链表的基本应用》中,大致的说明了;链表的基本算法和概念,但这也只是书面的介绍而已,而它到底怎么用连我自己也不是很清楚,所以现在在练一些可以用链表来做题的题目,做了几道题,觉得还可以,所以现在...
  • deardeer4869
  • deardeer4869
  • 2015年04月25日 23:44
  • 1177
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:linux内核二叉查找树的应用
举报原因:
原因补充:

(最多只允许输入30个字)