精华内容
下载资源
问答
  • 同时基数树也是按照字典顺序来组织叶节点的,这种特点使之适合持久化改造,加上他的多道特点,灵活性较强,适合作为区块链的基础数据结构,构建持久性区块时较好的映射各类数据集合。 Nginx基数树的实现  Nginx中...
  • 同时基数树也是按照字典顺序来组织叶节点的,这种特点使之适合持久化改造,加上他的多道特点,灵活性较强,适合作为区块链的基础数据结构,构建持久性区块时较好的映射各类数据集合。 Nginx基数树的实现 Nginx中...

    基数树介绍

           基数树也叫做压缩前缀树,是一种多叉搜索树,对比其他结构跟节省空间。基数树常见于IP路由检索,文本文档的的倒排索引等场景中。同时基数树也是按照字典顺序来组织叶节点的,这种特点使之适合持久化改造,加上他的多道特点,灵活性较强,适合作为区块链的基础数据结构,构建持久性区块时较好的映射各类数据集合。

    Nginx基数树的实现
            Nginx中基数树的实现是一种二叉查找树,具备二叉查找树的所有优点,同时避免了红黑树增删数据是需要通过自身旋转来维持平衡,因此他具有更快的插入、删除速度和更高的内存空间利用率。基数树的key兼顾唯一标识和树平衡维护的功能。的每个节点的key关键字先转换为32为二进制数,然后从左至右开始,0进入左子树,1进入右子树,节点插入的同时自动完成二叉树平衡的维护。
    1.数据结构

    struct ngx_radix_node_s {
        ngx_radix_node_t  *right;/*右孩子节点*/
        ngx_radix_node_t  *left;/*左孩子节点*/
        ngx_radix_node_t  *parent;/*父节点*/
        uintptr_t          value;/*用户自定义结构的数据指针*/
    };

            ngx_radix_tree_t是Nginx基数树的管理和操作类,实现了内存的自己管理。已经分配但是未使用的节点交给free变量,当需要使用节点的时候优先在free变量中重用已有的节点。

    typedef struct {
        ngx_radix_node_t  *root;/*根节点*/
        ngx_pool_t        *pool;/*内存池*/
        ngx_radix_node_t  *free;/*空闲节点*/
        char              *start;/*已分配内存未使用的首地址,也就是下个节点待分配内存的起始地址*/
        size_t             size;/*空闲内存的大小*/
    } ngx_radix_tree_t;

    2.基数树的创建
              基数树的创建和二叉树的创建没有太大的不同,按照基数树的规则一一实现就好。。Nginx基数树中为了减少二叉树的高度(压缩前缀树,压缩就体现在消除不需要的节点带来的分支)使用了掩码。通过掩码来决定树的高度,具体逻辑常见代码注释。

    ngx_radix_tree_t *
    ngx_radix_tree_create(ngx_pool_t *pool, ngx_int_t preallocate)
    {
        uint32_t           key, mask, inc;
        ngx_radix_tree_t  *tree;
    
        tree = ngx_palloc(pool, sizeof(ngx_radix_tree_t));
        if (tree == NULL) {
            return NULL;
        }
    
        tree->pool = pool;
        tree->free = NULL;
        tree->start = NULL;
        tree->size = 0;
    
        tree->root = ngx_radix_alloc(tree);
        if (tree->root == NULL) {
            return NULL;
        }
    
        tree->root->right = NULL;
        tree->root->left = NULL;
        tree->root->parent = NULL;
        tree->root->value = NGX_RADIX_NO_VALUE;
    
        if (preallocate == 0) {
            return tree;
        }
    
        /*
         * Preallocation of first nodes : 0, 1, 00, 01, 10, 11, 000, 001, etc.
         * increases TLB hits even if for first lookup iterations.
         * On 32-bit platforms the 7 preallocated bits takes continuous 4K,
         * 8 - 8K, 9 - 16K, etc.  On 64-bit platforms the 6 preallocated bits
         * takes continuous 4K, 7 - 8K, 8 - 16K, etc.  There is no sense to
         * to preallocate more than one page, because further preallocation
         * distributes the only bit per page.  Instead, a random insertion
         * may distribute several bits per page.
         *
         * Thus, by default we preallocate maximum
         *     6 bits on amd64 (64-bit platform and 4K pages)
         *     7 bits on i386 (32-bit platform and 4K pages)
         *     7 bits on sparc64 in 64-bit mode (8K pages)
         *     8 bits on sparc64 in 32-bit mode (8K pages)
         */
        /**/
    	/*1.根据系统电脑处理器架构决定基数树的最高层数*/
        if (preallocate == -1) {
            switch (ngx_pagesize / sizeof(ngx_radix_node_t)) {
    
            /* amd64 */
            case 128:
                preallocate = 6;
                break;
    
            /* i386, sparc64 */
            case 256:
                preallocate = 7;
                break;
    
            /* sparc64 in 32-bit mode */
            default:
                preallocate = 8;
            }
        }
    
        mask = 0;
        inc = 0x80000000;
        /*2.掩码初始0层,然后循环处理器架构层次,掩码依次右移逐层增加*/
        while (preallocate--) {
    
            key = 0;
            mask >>= 1;
    		/*2.1异或0x80000000保证掩码最高位是1*/
            mask |= 0x80000000;/*转换二进制1000 0000 0000 0000 0000 0000 0000 0000 */
           /*2.2基数树的首先遍历树的深度,如果为1,向右子树搜索,否则向左子树搜索,如果找到位置有结点,则直接覆盖。否则,则依次创建沿途结点(0或1)并插入在树中。*/
            do {
                if (ngx_radix32tree_insert(tree, key, mask, NGX_RADIX_NO_VALUE)
                    != NGX_OK)
                {
                    return NULL;
                }
    
                key += inc;
    
            } while (key);
    
            inc >>= 1;
        }
        /*返回构建好的树*/
        return tree;
    }

    3.基数树的查找
          根据基数树的规则,从根节点开始遍历,遇到0遍历左子树,遇到1遍历右子树,最后返回查询结果。

    uintptr_t
    ngx_radix32tree_find(ngx_radix_tree_t *tree, uint32_t key)
    {
        uint32_t           bit;
        uintptr_t          value;
        ngx_radix_node_t  *node;
    
        bit = 0x80000000;
        value = NGX_RADIX_NO_VALUE;
        node = tree->root;
    
        while (node) {
            if (node->value != NGX_RADIX_NO_VALUE) {
                value = node->value;
            }
    
            if (key & bit) {
                node = node->right;
    
            } else {
                node = node->left;
            }
    
            bit >>= 1;
        }
    
        return value;
    }

            Nginx的基数树要求每个节点key必须可以转换为32位整型,并且因为需要自己管理内存(无形中提高了使用的难度),所以总的来说,即使在Nginx中应用也不广泛。当然这个也没什么奇怪的,基数树解决的主要问题还是字典问题,而字典问题其实关联数组,Hash散列表都可以很好的解决这个问题,这样基数树不常用也就不难理解了。

    展开全文
  • 数据结构基数树(radix_tree)基数树(radix_tree)一,基数树数据的结构的介绍二, 基数树应用场景介绍三,基数树实现总结: 基数树(radix_tree) 基数树数据结构是在字典树(trie_tree)的原理上优化过来的, ...

    基数树(radix_tree)

    基数树数据结构是在字典树(trie_tree)的原理上优化过来的, 是更加合理的使用内存和查询的效率。

    一,基数树数据的结构的介绍

    基数树数据结构是字典树上进行可以前项压缩的数据优化的,基本和字典树数是一样的当是有区别的是进行内存优化的操作。基数树通常使用在ip地址保存操作。

    1, 没有进行前项压缩的结构

    在这里插入图片描述

    2, 前项压缩的操作结构图

    蓝色节点下面都黄色节点没有蓝色的节点就进行压缩的工作

    在这里插入图片描述

    二, 基数树应用场景介绍

    应用用于IP 路由的映射关系中。长整型的哈希冲突问题解决 (nginx,redis,linux中都使用到了)

    三,基数树实现

    1, 在创建基数树预先分配A类地址的数前8位

    预先分配字段是8的由来

    前缀长度 前缀 首字节
    A类地址 8位 0xxxxxxx 0~127
    B类地址 16位 10xxxxxx xxxxxxxx 128~191
    C类地址 24位 110xxxxx xxxxxxxx xxxxxxxx 192~223
    D类地址 不可用 1110xxxx xxxxxxxx xxxxxxxx xxxxxxxx 224~-239
    E类地址 不可用 1111xxxx xxxxxxxx xxxxxxxx xxxxxxxx 240~255

    对应代码

    	uint32 mask = 0x80000000;
    	
        uint32 preallocate = 8; //预先分配A类地址的数前8bit位
        while (preallocate--)
        {
            mask >>= 1;  // ip 地址掩码 是不同的
            mask |= 0x80000000;
            printf("mask = %u\n", mask);
        }
    

    output

     mask = 2147483648 ==> 1000 0000 0000 0000 0000 0000 0000 0000
     mask = 3221225472 ==> 1100 0000 0000 0000 0000 0000 0000 0000
     mask = 3758096384 ==> 1110 0000 0000 0000 0000 0000 0000 0000
     mask = 4026531840 ==> 1111 0000 0000 0000 0000 0000 0000 0000
     mask = 4160749568 ==> 1111 1000 0000 0000 0000 0000 0000 0000
     mask = 4227858432 ==> 1111 1100 0000 0000 0000 0000 0000 0000
     mask = 4261412864 ==> 1111 1110 0000 0000 0000 0000 0000 0000
     mask = 4278190080 ==> 1111 1111 0000 0000 0000 0000 0000 0000
     mask = 4286578688 ==> 1111 1111 1000 0000 0000 0000 0000 0000
    
    
    #include <stdio.h>
    #include  <stdlib.h>
    #include  <string.h>
    #include <assert.h>
    
    typedef unsigned int	uint32;
    typedef signed int		int32;
    
    /**
     * 默认地址
     */
    #define default_ip "127.0.0.1"
    
    // 基数树 应用场景ip地址映射
    struct cradix_tree_node
    {
        struct cradix_tree * left;
        struct cradix_tree * right;
        char * value; //实际的ip数据
    }cradix_tree;
    
    struct cradix_tree
    {
        struct cradix_tree_node * root;
        uint32  size; //预先分配默认A类地址的数前8位
    };
    
    
    /**
     * 基数树插入ip的地址映射关系
     * @param tree_ptr
     * @param key
     * @param mask
     * @param value
     * @return
     */
    void * cradix_tree_insert(struct cradix_tree * tree_ptr, uint32 key, uint32 mask, void * value)
    {
        assert(tree_ptr != NULL);
        if (!tree_ptr)
        {
            return NULL;
        }
    
        struct cradix_tree_node * cur_node = tree_ptr->root;
        struct cradix_tree_node * next_node = tree_ptr->root;
    
    
        //uint32 mask = 0;
        uint32 bit = 0x80000000;
        while (bit & mask)
        {
            //找到合适的位置插入
            if (bit & key)
            {
                next_node = (struct cradix_tree_node *)cur_node->right;
            }
            else
            {
                next_node = (struct cradix_tree_node *)cur_node->left;
            }
            if (!next_node)
            {
                break;
            }
            cur_node = next_node;
            bit >>= 1; //高位
        }
    
        if (next_node)
        {
            if (cur_node->value)
            {
                cur_node->value = value;
                return next_node;
            }
    
            cur_node->value = value;
            return cur_node;
        }
    
        while (bit & mask)
        {
            next_node = malloc(sizeof(struct cradix_tree_node));
            assert(next_node != NULL);
    
            {
                next_node->left = NULL;
                next_node->right = NULL;
                next_node->value = NULL;
            }
            if (bit & key)
            {
                cur_node->right = (void *)next_node;
            }
            else
            {
                cur_node->left = (void *)next_node;
            }
            bit >>= 1;
            cur_node = next_node;
            next_node = NULL;
        }
    
        if (cur_node)
        {
            cur_node->value = value;
        }
        return cur_node;
    }
    
    /**
     * 创建基数树
     * 预先分配A类地址的数前8bit位
     * @return
     */
    struct cradix_tree * create_radix_tree()
    {
        struct cradix_tree * tree_ptr = malloc(sizeof(struct cradix_tree));
        assert(tree_ptr != NULL);
        if (!tree_ptr)
        {
            return  NULL;
        }
    
        tree_ptr->root = NULL;
        tree_ptr->size = 8;
        tree_ptr->root = malloc(sizeof(struct cradix_tree_node));
        assert(tree_ptr->root != NULL);
        if(!tree_ptr->root)
        {
            return NULL;
        }
        {
            tree_ptr->root->left = NULL;
            tree_ptr->root->right = NULL;
            tree_ptr->root->value = NULL;
        }
    
    
        //建立基数树高度为 32为ip地址的树高度
    
        uint32 inc = 0x80000000; // 对应32位 1000 0000 0000 0000 0000 0000  0000 0000
        uint32 key = 0;
        uint32 mask = 0; //掩码
    
        uint32 preallocate = tree_ptr->size; //预先分配A类地址的数前8bit位
        while (preallocate--)
        {
            key = 0;
            mask >>= 1;  // ip 地址掩码 是不同的
            mask |= 0x80000000;
            // 1000 0000 0000 0000 0000 0000 0000 0000  0000 0000
            /**
               *   ip的掩码 A类地址[8bit]  B类地址[16bit] C类地址[24bit] D,E类型地址不可用
             *   | 类   | 前缀长度   |  前缀
             *   |A类地址|  8位      | 0xxxxxxx                                              |
             *   |B类地址| 16位      | 10xxxxxx   xxxxxxxx                                   |
             *   |C类地址| 24位      | 110xxxxx   xxxxxxxx   xxxxxxxx                        |
             *   |D类地址| 不可用     | 1110xxxx   xxxxxxxx   xxxxxxxx     xxxxxxxx           |
             *   |E类地址| 不可用     | 1111xxxx   xxxxxxxx   xxxxxxxx     xxxxxxxx  			|
             *   
             * mask 对应 就是 A类地址的前8位bit位(默认第32位是1在代码中)
                mask = 2147483648 ==> 1000 0000 0000 0000 0000 0000 0000 0000
                mask = 3221225472 ==> 1100 0000 0000 0000 0000 0000 0000 0000
                mask = 3758096384 ==> 1110 0000 0000 0000 0000 0000 0000 0000
                mask = 4026531840 ==> 1111 0000 0000 0000 0000 0000 0000 0000
                mask = 4160749568 ==> 1111 1000 0000 0000 0000 0000 0000 0000
                mask = 4227858432 ==> 1111 1100 0000 0000 0000 0000 0000 0000
                mask = 4261412864 ==> 1111 1110 0000 0000 0000 0000 0000 0000
                mask = 4278190080 ==> 1111 1111 0000 0000 0000 0000 0000 0000
                mask = 4286578688 ==> 1111 1111 1000 0000 0000 0000 0000 0000
             */
            //遍历从树的顶层向下遍历
            do
            {
                //插入当前节点的数据
                cradix_tree_insert(tree_ptr, key, mask, NULL);
                //这边看出来了吗?  就加到内存溢出 就变成'0'啊
                key += inc; // 举例  == [0x80000000 + 0x80000000] = [0x100000000]  但是无符号int类型放不下这个数据所以 就是 [0x00000000] 高位溢出
            } while (key);
            inc >>= 1;
        }
    
        return tree_ptr;
    }
    
    /**
     * 基数树的查找O(logn)
     * @param tree_ptr
     * @param key
     * @return
     */
    char * cradix_tree_find(struct cradix_tree * tree_ptr, uint32 key)
    {
        assert(tree_ptr != NULL);
        if (!tree_ptr)
        {
            return NULL;
        }
        uint32 bit = 0x80000000;
        struct cradix_tree_node * cur_node = tree_ptr->root;
        void * ptr = NULL;
        while (cur_node)
        {
            if (cur_node->value)
            {
                ptr = cur_node->value;
            }
            if (key & bit)
            {
                cur_node = (struct cradix_tree_node *)cur_node->right;
            }
            else
            {
                cur_node = (struct cradix_tree_node *)cur_node->left;
            }
            bit >>= 1;
        }
        if (!ptr)
        {
            return default_ip;
        }
        return ptr;
    }
    
    /**
     * 递归释放基数树的节点的内存
     * @param tree_ptr
     */
    void cradix_tree_node_destroy(struct cradix_tree_node *tree_ptr)
    {
        if (!tree_ptr)
        {
            return;
        }
        cradix_tree_node_destroy((struct cradix_tree_node *)tree_ptr->left);
        if (tree_ptr->left)
        {
            free(tree_ptr->left);
            tree_ptr->left = NULL;
        }
        cradix_tree_node_destroy((struct cradix_tree_node *)tree_ptr->right);
        if (tree_ptr->right)
        {
            free(tree_ptr->right);
            tree_ptr->right = NULL;
        }
    }
    
    /**
     * 释放基数树内存
     * @param tree_ptr
     */
    void cradix_tree_destroy(struct cradix_tree** tree_ptr)
    {
        assert(tree_ptr != NULL);
        if (!tree_ptr)
        {
            return ;
        }
    
        cradix_tree_node_destroy((struct cradix_tree_node *)(*tree_ptr)->root);
    
        if ((*tree_ptr))
        {
            if ((*tree_ptr)->root)
            {
                free((*tree_ptr)->root);
                (*tree_ptr)->root = NULL;
            }
            free((*tree_ptr));
            (*tree_ptr) = NULL;
    
        }
    }
    
    /**
     * 测试数据结构
     */
    struct ip_proxy {
        uint32  ip; // ip-> uint32
        uint32  mask; // 掩码
        char *  value; //映射的数据
    };
    
    /**2^0 = 1;
     * 2^1 = 2;
     * 2^2 = 4
     * 2^3 = 8
     * 2^4 = 16
     * 2^5 = 32
     * 2^6 = 64
     * 2^7 = 128
     *
     *
     * 测试数据
     *
     */
    struct ip_proxy ip_data[] =
    {         // 128 + 64 + 32 +15 = 96+15 +128 = 111+128 = 239  === 255 - 16 = 239
            { 4009754624/*1110 1111 0000 0000 0000 0000 0000 0000 == 239.0.0.0*/,  4278190080 /*1111 1111 0000 0000 0000 0000 0000 0000 === 255.0.0.0*/, "192.168.1.90"},
            { 4093640704,  4278190080, "192.168.1.91"},
            { 4160749568,  4278190080, "192.168.1.93"},
            { 4244635648, 4278190080, "wangyang"},
            { 3841982464, 4278190080, "shanghui"},
            { 2566914048, 4278190080, "beijing"}
    };
    
    
    
    //void show(struct cradix_tree_node * tree)
    //{
    //    if (!tree)
    //    {
    //        return;
    //    }
    //    printf(" vlaue %s\n", tree->value);
    //    show(tree->left);
    //    show(tree->right);
    //}
    
    
    
    int main(int argc, char *argv[])
    {
    
        struct cradix_tree * tree_ptr = create_radix_tree();
        if (!tree_ptr)
        {
            return -1;
        }
    
        for (int i = 0; i < (sizeof(ip_data) / sizeof(struct ip_proxy)); ++i)
        {
            cradix_tree_insert(tree_ptr, ip_data[i].ip, ip_data[i].mask, ip_data[i].value);
        }
        ///show(tree_ptr->root);
        for (int i = 0; i < (sizeof(ip_data) / sizeof(struct ip_proxy)); ++i)
        {
            printf("ip mask  = %u, ip_proxy = %s, new_ip_proxy = %s\n", ip_data[i].ip, ip_data[i].value, cradix_tree_find(tree_ptr, ip_data[i].ip) );
        }
        //默认代理ip地址
        printf("not ip mask  = %u, ip_proxy = %s, new_ip_proxy = %s\n", 23432, "", cradix_tree_find(tree_ptr, 23432) );
        cradix_tree_destroy(&tree_ptr);
        return EXIT_SUCCESS;
    
    

    总结:

    源码地址:https://github.com/chensongpoixs/cleet_code

    展开全文
  • Linux 内核里的数据结构——基数树 正如你所知道的,Linux内核提供了许多不同的库和函数,它们实现了不同的数据结构和算法。在这部分,我们将研究其中一种数据结构——基数树Radix tree。在 Linux 内核中,有两个...

    Linux 内核里的数据结构——基数树

    Trie

    正如你所知道的,Linux内核提供了许多不同的库和函数,它们实现了不同的数据结构和算法。在这部分,我们将研究其中一种数据结构——基数树Radix tree。在 Linux 内核中,有两个文件与基数树的实现和API相关:

    让我们先说说什么是 基数树 吧。基数树是一种 压缩的字典树compressed trie ,而字典树是实现了关联数组接口并允许以键值对 方式存储值的一种数据结构。这里的键通常是字符串,但可以使用任意数据类型。字典树因为它的节点而与 n叉树 不同。字典树的节点不存储键,而是存储单个字符的标签。与一个给定节点关联的键可以通过从根遍历到该节点获得。举个例子:

    
    
    1. +-----------+
    2. | |
    3. | " " |
    4. | |
    5. +------+-----------+------+
    6. | |
    7. | |
    8. +----v------+ +-----v-----+
    9. | | | |
    10. | g | | c |
    11. | | | |
    12. +-----------+ +-----------+
    13. | |
    14. | |
    15. +----v------+ +-----v-----+
    16. | | | |
    17. | o | | a |
    18. | | | |
    19. +-----------+ +-----------+
    20. |
    21. |
    22. +-----v-----+
    23. | |
    24. | t |
    25. | |
    26. +-----------+

    因此在这个例子中,我们可以看到一个有着两个键 go 和 cat 的 字典树 。压缩的字典树也叫做 基数树,它和 字典树 的不同之处在于,所有只有一个子节点的中间节点都被删除。

    Linux 内核中的基数树是把值映射到整形键的一种数据结构。include/linux/radix-tree.h文件中的以下结构体描述了基数树:

    
    
    1. struct radix_tree_root {
    2. unsigned int height;
    3. gfp_t gfp_mask;
    4. struct radix_tree_node __rcu *rnode;
    5. };

    这个结构体描述了一个基数树的根,它包含了3个域成员:

    • height - 树的高度;
    • gfp_mask - 告知如何执行动态内存分配;
    • rnode - 孩子节点指针.

    我们第一个要讨论的字段是 gfp_mask :

    底层内核的内存动态分配函数以一组标志作为 gfp_mask ,用于描述如何执行动态内存分配。这些控制分配进程的 GFP_ 标志拥有以下值:( GF_NOIO 标志)意味着睡眠以及等待内存,( __GFP_HIGHMEM 标志)意味着高端内存能够被使用,( GFP_ATOMIC 标志)意味着分配进程拥有高优先级并不能睡眠等等。

    • GFP_NOIO - 睡眠等待内存
    • __GFP_HIGHMEM - 高端内存能够被使用;
    • GFP_ATOMIC - 分配进程拥有高优先级并且不能睡眠;

    等等。

    下一个字段是rnode

    
    
    1. struct radix_tree_node {
    2. unsigned int path;
    3. unsigned int count;
    4. union {
    5. struct {
    6. struct radix_tree_node *parent;
    7. void *private_data;
    8. };
    9. struct rcu_head rcu_head;
    10. };
    11. /* For tree user */
    12. struct list_head private_list;
    13. void __rcu *slots[RADIX_TREE_MAP_SIZE];
    14. unsigned long tags[RADIX_TREE_MAX_TAGS][RADIX_TREE_TAG_LONGS];
    15. };

    这个结构体包含的信息有父节点中的偏移以及到底端(叶节点)的高度、子节点的个数以及用于访问和释放节点的字段成员。这些字段成员描述如下:

    • path - 父节点中的偏移和到底端(叶节点)的高度
    • count - 子节点的个数;
    • parent - 父节点指针;
    • private_data - 由树的用户使用;
    • rcu_head - 用于释放节点;
    • private_list - 由树的用户使用;

    radix_tree_node 的最后两个成员—— tags 和 slots 非常重要且令人关注。Linux 内核基数树的每个节点都包含了一组指针槽slots,槽里存储着指向数据的指针。在Linux内核基数树的实现中,空槽存储的是 NULL 。Linux内核中的基数树也支持标签tags,它与 radix_tree_node 结构体的 tags 字段相关联。有了标签,我们就可以对基数树中存储的记录以单个比特位bit进行设置。

    既然我们了解了基数树的结构,那么该是时候看一下它的API了。

    Linux内核基数树API

    我们从结构体的初始化开始。有两种方法初始化一个新的基数树。第一种是使用 RADIX_TREE 宏:

    
    
    1. RADIX_TREE(name, gfp_mask);

    正如你所看到的,我们传递了 name 参数,所以通过 RADIX_TREE 宏,我们能够定义和初始化基数树为给定的名字。RADIX_TREE 的实现很简单:

    
    
    1. #define RADIX_TREE(name, mask) \
    2. struct radix_tree_root name = RADIX_TREE_INIT(mask)
    3. #define RADIX_TREE_INIT(mask) { \
    4. .height = 0, \
    5. .gfp_mask = (mask), \
    6. .rnode = NULL, \
    7. }

    在 RADIX_TREE 宏的开始,我们使用给定的名字定义 radix_tree_root 结构体实例,并使用给定的 mask 调用 RADIX_TREE_INIT 宏。 而 RADIX_TREE_INIT 宏则是使用默认值和给定的mask对 radix_tree_root 结构体进行了初始化。

    第二种方法是手动定义radix_tree_root结构体,并且将它和mask传给 INIT_RADIX_TREE 宏:

    
    
    1. struct radix_tree_root my_radix_tree;
    2. INIT_RADIX_TREE(my_tree, gfp_mask_for_my_radix_tree);

    INIT_RADIX_TREE 宏的定义如下:

    
    
    1. #define INIT_RADIX_TREE(root, mask) \
    2. do { \
    3. (root)->height = 0; \
    4. (root)->gfp_mask = (mask); \
    5. (root)->rnode = NULL; \
    6. } while (0)

    RADIX_TREE_INIT宏所做的初始化工作一样,INIT_RADIX_TREE 宏使用默认值和给定的 mask 完成初始化工作。

    接下来是用于向基数树插入和删除数据的两个函数:

    • radix_tree_insert;
    • radix_tree_delete;

    第一个函数 radix_tree_insert 需要3个参数:

    • 基数树的根;
    • 索引键;
    • 插入的数据;

    radix_tree_delete 函数需要和 radix_tree_insert 一样的一组参数,但是不需要传入要删除的数据。

    基数树的搜索以两种方法实现:

    • radix_tree_lookup;
    • radix_tree_gang_lookup;
    • radix_tree_lookup_slot.

    第一个函数radix_tree_lookup需要两个参数:

    • 基数树的根;
    • 索引键;

    这个函数尝试在树中查找给定的键,并返回和该键相关联的记录。第二个函数 radix_tree_gang_lookup 有以下的函数签名:

    
    
    1. unsigned int radix_tree_gang_lookup(struct radix_tree_root *root,
    2. void **results,
    3. unsigned long first_index,
    4. unsigned int max_items);

    它返回的是记录的个数。 results 中的结果,按键排序,并从第一个索引开始。返回的记录个数将不会超过max_items 的值。

    最后一个函数radix_tree_lookup_slot将会返回包含数据的指针槽。

    链接




    本文来自云栖社区合作伙伴“Linux中国”
    原文发布时间为:2013-04-02.
    展开全文
  • Linux 内核里的数据结构 —— 基数树 基数树 Radix tree Linux内核基数树API 链接 Linux 内核里的数据结构 —— 基数树 基数树 Radix tree 正如你所知道的,Linux内核提供了许多不同的库和函数,它们实.....

    Linux 内核里的数据结构 —— 基数树

    基数树 Radix tree

    正如你所知道的,Linux内核提供了许多不同的库和函数,它们实现了不同的数据结构和算法。在这部分,我们将研究其中一种数据结构——基数树 Radix tree。在 Linux 内核中,有两个文件与基数树的实现和API相关:

    让我们先说说什么是 基数树 吧。基数树是一种 压缩的字典树 (compressed trie) ,而字典树是实现了关联数组接口并允许以 键值对 方式存储值的一种数据结构。这里的键通常是字符串,但可以使用任意数据类型。字典树因为它的节点而与 n叉树 不同。字典树的节点不存储键,而是存储单个字符的标签。与一个给定节点关联的键可以通过从根遍历到该节点获得。举个例子:

                   +-----------+
                   |           |
                   |    " "    |
                   |           |
            +------+-----------+------+
            |                         |
            |                         |
       +----v------+            +-----v-----+
       |           |            |           |
       |    g      |            |     c     |
       |           |            |           |
       +-----------+            +-----------+
            |                         |
            |                         |
       +----v------+            +-----v-----+
       |           |            |           |
       |    o      |            |     a     |
       |           |            |           |
       +-----------+            +-----------+
                                      |
                                      |
                                +-----v-----+
                                |           |
                                |     t     |
                                |           |
                                +-----------+

    因此在这个例子中,我们可以看到一个有着两个键 gocat字典树 。压缩的字典树也叫做 基数树 ,它和 字典树 的不同之处在于,所有只有一个子节点的中间节点都被删除。

    Linux 内核中的基数树是把值映射到整形键的一种数据结构。include/linux/radix-tree.h文件中的以下结构体描述了基数树:

    struct radix_tree_root {
             unsigned int            height;
             gfp_t                   gfp_mask;
             struct radix_tree_node  __rcu *rnode;
    };

    这个结构体描述了一个基数树的根,它包含了3个域成员:

    • height - 树的高度;
    • gfp_mask - 告知如何执行动态内存分配;
    • rnode - 孩子节点指针.

    我们第一个要讨论的字段是 gfp_mask

    底层内核的内存动态分配函数以一组标志作为 gfp_mask ,用于描述如何执行动态内存分配。这些控制分配进程的 GFP_ 标志拥有以下值:( GF_NOIO 标志)意味着睡眠以及等待内存,( __GFP_HIGHMEM 标志)意味着高端内存能够被使用,( GFP_ATOMIC 标志)意味着分配进程拥有高优先级并不能睡眠等等。

    • GFP_NOIO - 睡眠等待内存
    • __GFP_HIGHMEM - 高端内存能够被使用;
    • GFP_ATOMIC - 分配进程拥有高优先级并且不能睡眠;

    等等。

    下一个字段是rnode

    struct radix_tree_node {
            unsigned int    path;
            unsigned int    count;
            union {
                    struct {
                            struct radix_tree_node *parent;
                            void *private_data;
                    };
                    struct rcu_head rcu_head;
            };
            /* For tree user */
            struct list_head private_list;
            void __rcu      *slots[RADIX_TREE_MAP_SIZE];
            unsigned long   tags[RADIX_TREE_MAX_TAGS][RADIX_TREE_TAG_LONGS];
    };

    这个结构体包含的信息有父节点中的偏移以及到底端(叶节点)的高度、子节点的个数以及用于访问和释放节点的字段成员。这些字段成员描述如下:

    • path - 父节点中的偏移和到底端(叶节点)的高度
    • count - 子节点的个数;
    • parent - 父节点指针;
    • private_data - 由树的用户使用;
    • rcu_head - 用于释放节点;
    • private_list - 由树的用户使用;

    radix_tree_node 的最后两个成员—— tagsslots 非常重要且令人关注。Linux 内核基数树的每个节点都包含了一组指针槽( slots ),槽里存储着指向数据的指针。在Linux内核基数树的实现中,空槽存储的是 NULL 。Linux内核中的基数树也支持标签( tags ),它与 radix_tree_node 结构体的 tags 字段相关联。有了标签,我们就可以对基数树中存储的记录以单个比特位( bit )进行设置。

    既然我们了解了基数树的结构,那么该是时候看一下它的API了。

    Linux内核基数树API

    我们从结构体的初始化开始。有两种方法初始化一个新的基数树。第一种是使用 RADIX_TREE 宏:

    RADIX_TREE(name, gfp_mask);

    正如你所看到的,我们传递了 name 参数,所以通过 RADIX_TREE 宏,我们能够定义和初始化基数树为给定的名字。RADIX_TREE 的实现很简单:

    #define RADIX_TREE(name, mask) \
             struct radix_tree_root name = RADIX_TREE_INIT(mask)
    
    #define RADIX_TREE_INIT(mask)   { \
            .height = 0,              \
            .gfp_mask = (mask),       \
            .rnode = NULL,            \
    }

    RADIX_TREE 宏的开始,我们使用给定的名字定义 radix_tree_root 结构体实例,并使用给定的 mask 调用 RADIX_TREE_INIT 宏。 而 RADIX_TREE_INIT 宏则是使用默认值和给定的mask对 radix_tree_root 结构体进行了初始化。

    第二种方法是手动定义radix_tree_root结构体,并且将它和mask传给 INIT_RADIX_TREE 宏:

    struct radix_tree_root my_radix_tree;
    INIT_RADIX_TREE(my_tree, gfp_mask_for_my_radix_tree);

    INIT_RADIX_TREE 宏的定义如下:

    #define INIT_RADIX_TREE(root, mask)  \
    do {                                 \
            (root)->height = 0;          \
            (root)->gfp_mask = (mask);   \
            (root)->rnode = NULL;        \
    } while (0)

    RADIX_TREE_INIT宏所做的初始化工作一样,INIT_RADIX_TREE 宏使用默认值和给定的 mask 完成初始化工作。

    接下来是用于向基数树插入和删除数据的两个函数:

    • radix_tree_insert;
    • radix_tree_delete;

    第一个函数 radix_tree_insert 需要3个参数:

    • 基数树的根;
    • 索引键;
    • 插入的数据;

    radix_tree_delete 函数需要和 radix_tree_insert 一样的一组参数,但是不需要传入要删除的数据。

    基数树的搜索以两种方法实现:

    • radix_tree_lookup;
    • radix_tree_gang_lookup;
    • radix_tree_lookup_slot.

    第一个函数radix_tree_lookup需要两个参数:

    • 基数树的根;
    • 索引键;

    这个函数尝试在树中查找给定的键,并返回和该键相关联的记录。第二个函数 radix_tree_gang_lookup 有以下的函数签名:

    unsigned int radix_tree_gang_lookup(struct radix_tree_root *root,
                                        void **results,
                                        unsigned long first_index,
                                        unsigned int max_items);

    它返回的是记录的个数。 results 中的结果,按键排序,并从第一个索引开始。返回的记录个数将不会超过 max_items 的值。

    最后一个函数radix_tree_lookup_slot将会返回包含数据的指针槽。

    链接


    via: https://github.com/0xAX/linux-insides/blob/master/DataStructures/radix-tree.md

    转载于:https://www.cnblogs.com/cposture/p/9029030.html

    展开全文
  •  Radix树,即基数树,也称压缩前缀树,是一种提供key-value存储查找的数据结构。与Trie不同的是,它对Trie树进行了空间优化,只有一个子节点的中间节点将被压缩。同样的,Radix树的插入、查询、删除操作的时间...
  • 1 nginx的基数树简介 基数树是一种二叉查找树,它具备二叉查找树的所有优点:检索、插入、删除节点速度快,支持范围查找,支持遍历等。在nginx中仅geo模块使用了基数树。nginx的基数树使用ngx_radix_tree_t这个...
  • 菜鸟nginx源码剖析数据结构篇(五) 基数树 ngx_radix_tree_t   Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csdn.net/chen19870707 Date:October 28h, 2014 ...
  • 在读gin路由原理时了解到gin 路由应用数据结构基数树实现,有低内存、高效率的特点。 在此记录一下基数树的原理,同时也对比一下很相近的前缀树。 基数树: 前缀树: 引用 ......
  • 基数树的简单实现

    2019-12-07 02:38:19
    基数树是一种比较节省空间的树结构,下图展示了基数树结构,其中key是树的构建方式,在这里,key是一个32位的整数,为了避免层数过深,所以使用两位代表子节点的索引,基数树就是依据二进制串来生成树结构。...
  • 在有序数据结构的空间中,特别有趣,因为它们的高度和时间复杂度取决于密钥长度(k)而不是中已经存在的密钥数(n)。 在数据集非常庞大的时代,当n的增长速度快于k时,具有与n无关的时间复杂度非常有吸引力。 ...
  • 数据结构算法

    千次阅读 2019-03-23 15:53:09
    1.(英语:tree)是一种抽象数据类型(ADT)或是实作这种抽象数据类型的数据结构,用来模拟具有树状结构性质的数据集合。它是由n(n&amp;gt;=1)个有限节点组成一个具有层次关系的集合。把它叫做“”是因为...
  • 基数树

    2020-03-17 21:50:54
    喜欢这篇文章吗?喜欢的话去看博主的置顶博客,即可依据分类找到此文章的原版...title: 基数树 mathjax: true date: 2020-03-16 16:19:30 categories: [myself_library,datastructure,tree,trie] tags: [myself_lib...
  • 基数树结构---radix_tree

    千次阅读 2018-04-28 16:09:54
    最近在学习linux中内存管理相关的章节,其中页缓存相关的结构体中遇到了成员数据结构radix_tree_root和radix_tree_node,由于以前没有遇见过这两种数据结构,因此在此处针对这两种数据结构在linux内核的内存管理中的...
  • 是各种数据结构中最重头戏的结构。的各种分类及其子类的各种特征,也是在算法研究和实践中最有价值的学问。本人基于自己的学识和见闻,打算写一篇的前世今生和的大家族的介绍,由于自己的各种局限,难免有...
  • JAVA实现 1.堆排序 图解堆排序 2.形排序 3.计数排序 4.基数排序 对于该博主的计数排序的优化,可先写一个找最大值的方法,从而由该最大值确定需要比较多少位 ...
  • linux 基数树

    千次阅读 2012-09-07 10:09:28
    Linux基数树(radix tree)是将指针与long整数键值相关联的机制,它存储有效率,并且可快速查询,用于指针与整数值的映射(如:IDR机制)、内存管理等。 IDR(ID Radix)机制是将对象的身份鉴别号整数值ID与...
  • 数据结构--类型及的基本概念

    千次阅读 2016-12-27 12:23:22
    数据结构的基本类型。 的基本概念 的基本操作
  • 字典树和基数树

    2019-08-17 23:04:53
    字典是一种前缀查找,在前缀匹配查找中应用比较多,查找的层级取决于字符串长度,时间复杂度O(1),但是他要求每个节点要有26各分支,所以空间开销比较高,是一种典型的以空间换时间的数据结构。 2、实现原理...
  • 前言 推出一个新系列,《看图轻松...Radix树,即基数树,也称压缩前缀树,是一种提供key-value存储查找的数据结构。与Trie不同的是,它对Trie树进行了空间优化,只有一个子节点的中间节点将被压缩。同样的,Radix...
  • radix tree 基数树

    千次阅读 2012-09-08 17:03:59
    Linux基数树(radix tree)是将指针与long整数键值相关联的机制,它存储有效率,并且可快速查询,用于指针与整数值的映射(如:IDR机制)、内存管理等。 IDR(ID Radix)机制是将对象的身份鉴别号整数值ID与对象...
  • RadixTree(基数树

    千次阅读 2015-11-06 07:40:30
    1. 基数树概述 对于长整型数据的映射,如何解决Hash冲突和Hash表大小的设计是一个很头疼的问题。 radix树就是针对这种稀疏的长整型数据查找,能快速且节省空间地完成映射。借助于Radix树,我们可以实现对于长整型...
  • 数据结构基数排序的哈希表

    千次阅读 2010-08-09 05:49:00
    为了实现计算机智能化, 需要在超巨量的空间内快速地存储、检索、匹配和修改数据,传统的哈希表算法根本不适用, 而且在很多应用场合需要一种按整数方式自然排序的哈希容器,但可惜数据结构理论和 实践中都没有...
  • Nginx学习(10)—基数树

    千次阅读 2014-04-28 12:00:07
    基数树ngx_radix_tree_t 基数树也是一种二叉查找树,它要求存储的每个节点必须以32位整型作为任意两节点的唯一标识。另外,基数树与红黑树不同的一点:ngx_radix_tree_t会负责分配每个节点占用的内存。也因为这一点...
  • 艺术 高度基于自适应基数树的高压缩基数树() 特征 非常便携-干净的C89实施 设计为主索引数据结构-上面的论文描述了不完全拥有其键的二级索引 非递归更新算法
  • 基数树(radix tree)

    千次阅读 2016-06-06 10:01:47
    Linux基数树(radix tree)是将指针与long整数键值相关联的机制,它存储有效率,并且可快速查询,用于指针与整数值的映射(如:IDR机制)、内存管理等。 IDR(ID Radix)机制是将对象的身份鉴别号整数值ID与对象...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 24,796
精华内容 9,918
关键字:

数据结构基数树

数据结构 订阅