zoukankan      html  css  js  c++  java
  • C高级 框架开发中红黑树结构

    引言  -- 红黑树历史

      红黑树是数据结构学习中一道卡. 底层库容器中必不可少的算法. 历经各种实战运用,性能有保障. 同样红黑树不好理解, 就算理解了, 代码也不好写.

    就算写了, 工程库也难构建. 关于红黑树基础讲解推荐看下面博主的红黑树博文系列,感觉不错.

      红黑树(一)之 原理和算法详细介绍 

    对于红黑树小背景简介摘抄如下:

      红黑树英语:Red–black tree)是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组。它是在1972年由鲁道夫·贝尔发明的,他称之为"对称二叉B树",它现代的名字是在Leo J. Guibas和Robert Sedgewick于1978年写的一篇论文中获得的。它是复杂的,但它的操作有着良好的最坏情况运行时间,并且在实践中是高效的:它可以在O(log n)时间内做查找,插入和删除,这里的n是树中元素的数目。

    对于红黑树更加详细的历史参照下面资料.

      红黑树 https://zh.wikipedia.org/wiki/%E7%BA%A2%E9%BB%91%E6%A0%91

    本文重点介绍工程开发中, 红黑树工程基库的封装.直接用现成的最爽.

    前言  -- 红黑树工程库源码

       一言不合就上源码! 

    rbtree.h

    #ifndef _H_RBTREE
    #define _H_RBTREE
    
    struct rbnode {
        unsigned long    parent_color;
        struct rbnode * right;
        struct rbnode * left;
    };
    
    typedef void * (* new_f)(void *);
    typedef int (* cmp_f)(const void *, const void *);
    typedef void (* die_f)(void *);
    
    typedef struct {
        struct rbnode * root;
        new_f new;
        cmp_f cmp;
        die_f die;
    } * rbtree_t;
    
    /*
     * 每个想使用红黑树的结构, 需要在头部插入下面宏. 
     * 例如 :
        struct person {
            _HEAD_RBTREE;
            ... // 自定义信息
        };
     */
    #define _HEAD_RBTREE    struct rbnode __node
    
    /*
     * 创建一颗红黑树头结点 
     * new        : 注册创建结点的函数
     * cmp        : 注册比较的函数
     * die        : 注册程序销毁函数
     *            : 返回创建好的红黑树结点
     */
    extern rbtree_t rb_new(new_f new, cmp_f cmp, die_f die);
    
    /*
     * 插入一个结点, 会插入 new(pack)
     * tree        : 红黑树头结点
     * pack        : 待插入的结点当cmp(x, pack) 右结点
     */
    extern void rb_insert(rbtree_t tree, void * pack);
    
    /*
     * 删除能和pack匹配的结点
     * tree        : 红黑树结点
     * pack        : 当cmp(x, pack) 右结点
     */
    extern void rb_remove(rbtree_t tree, void * pack);
    
    /*
     * 得到红黑树中匹配的结点
     * tree        : 匹配的结点信息
     * pack        : 当前待匹配结点, cmp(x, pack)当右结点处理
     */
    extern void * rb_get(rbtree_t tree, void * pack);
    
    /*
     * 销毁这颗二叉树
     * tree        : 当前红黑树结点
     */
    extern void rb_die(rbtree_t tree);
    
    #endif /* _H_RBTREE */

    rbtree.c

    #include "rbtree.h"
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    /*
     * 操作辅助宏, 得到红黑树中具体父结点, 颜色. 包括详细设置信息
     * r    : 头结点
     * p    : 父结点新值
     * c    : 当前颜色
     */
    #define rb_parent(r)        ((struct rbnode *)((r)->parent_color & ~3))
    #define rb_color(r)            ((r)->parent_color & 1)
    #define rb_is_red(r)        (!rb_color(r))
    #define rb_is_black(r)        rb_color(r)
    #define rb_set_black(r)        (r)->parent_color |= 1
    #define rb_set_red(r)        (r)->parent_color &= ~1
    
    static inline void rb_set_parent(struct rbnode * r, struct rbnode * p) {
         r->parent_color = (r->parent_color & 3) | (unsigned long)p;
    }
    
    static inline void rb_set_color(struct rbnode * r, int color) {
         r->parent_color = (r->parent_color & ~1) | (1 & color);
    }
    
    static inline int _rb_cmp(const void * ln, const void * rn) {
        return (const char *)ln - (const char *)rn;
    }
    
     /*
      * 创建一颗红黑树头结点
      * new        : 注册创建结点的函数
      * cmp        : 注册比较的函数
      * die        : 注册程序销毁函数
      *            : 返回创建好的红黑树结点
      */
    rbtree_t 
    rb_new(new_f new, cmp_f cmp, die_f die) {
        rbtree_t tree = malloc(sizeof(*tree));
        if(NULL == tree) {
            fprintf(stderr, "rb_new malloc is error!");
            return NULL;    
        }
        
        tree->root = NULL;
        tree->new = new;
        tree->cmp = cmp ? cmp : _rb_cmp;
        tree->die = die;
    
        return tree;
    }
    
    static inline struct rbnode * _rb_new(rbtree_t tree, void * pack) {
        struct rbnode * node = tree->new ? tree->new(pack) : pack;
        memset(node, 0, sizeof(struct rbnode));
        return node;
    }
    
    /* 
     * 对红黑树的节点(x)进行左旋转
     *
     * 左旋示意图(对节点x进行左旋):
     *      px                              px
     *     /                               /
     *    x                               y                
     *   /        --(左旋)-->           /                 #
     *  lx   y                          x  ry     
     *     /                          /  
     *    ly   ry                     lx  ly  
     *
     */
    static void _rbtree_left_rotate(rbtree_t tree, struct rbnode * x) {
        // 设置x的右孩子为y
        struct rbnode * y = x->right;
        struct rbnode * xparent = rb_parent(x);
    
        // 将 “y的左孩子” 设为 “x的右孩子”;
        x->right = y->left;
        // 如果y的左孩子非空,将 “x” 设为 “y的左孩子的父亲”
        if (y->left != NULL)
            rb_set_parent(y->left, x);
    
        // 将 “x的父亲” 设为 “y的父亲”
        rb_set_parent(y, xparent);
    
        if (xparent == NULL)
            tree->root = y;            // 如果 “x的父亲” 是空节点,则将y设为根节点
        else {
            if (xparent->left == x)
                xparent->left = y;     // 如果 x是它父节点的左孩子,则将y设为“x的父节点的左孩子”
            else
                xparent->right = y;    // 如果 x是它父节点的左孩子,则将y设为“x的父节点的左孩子”
        }
        
        // 将 “x” 设为 “y的左孩子”
        y->left = x;
        // 将 “x的父节点” 设为 “y”
        rb_set_parent(x, y);
    }
    
    /* 
     * 对红黑树的节点(y)进行右旋转
     *
     * 右旋示意图(对节点y进行左旋):
     *            py                               py
     *           /                                /
     *          y                                x                  
     *         /        --(右旋)-->            /                       #
     *        x   ry                           lx   y  
     *       /                                    /                    #
     *      lx  rx                                rx  ry
     * 
     */
    static void _rbtree_right_rotate(rbtree_t tree, struct rbnode * y) {
        // 设置x是当前节点的左孩子。
        struct rbnode * x = y->left;
        struct rbnode * yparent = rb_parent(y);
    
        // 将 “x的右孩子” 设为 “y的左孩子”;
        y->left = x->right;
        // 如果"x的右孩子"不为空的话,将 “y” 设为 “x的右孩子的父亲”
        if (x->right != NULL)
            rb_set_parent(x->right, y);
    
        // 将 “y的父亲” 设为 “x的父亲”
        rb_set_parent(x, yparent);
        if (yparent == NULL) 
            tree->root = x;                // 如果 “y的父亲” 是空节点,则将x设为根节点
        else {
            if (y == yparent->right)
                yparent->right = x;        // 如果 y是它父节点的右孩子,则将x设为“y的父节点的右孩子”
            else
                yparent->left = x;        // (y是它父节点的左孩子) 将x设为“x的父节点的左孩子”
        }
    
        // 将 “y” 设为 “x的右孩子”
        x->right = y;
        // 将 “y的父节点” 设为 “x”
        rb_set_parent(y, x);
    }
    
    /*
     * 红黑树插入修正函数
     *
     * 在向红黑树中插入节点之后(失去平衡),再调用该函数;
     * 目的是将它重新塑造成一颗红黑树。
     *
     * 参数说明:
     *     tree 红黑树的根
     *     node 插入的结点        // 对应《算法导论》中的z
     */
    static void _rbtree_insert_fixup(rbtree_t tree, struct rbnode * node) {
        struct rbnode * parent, * gparent, * uncle;
    
        // 若“父节点存在,并且父节点的颜色是红色”
        while ((parent = rb_parent(node)) && rb_is_red(parent)) {
            gparent = rb_parent(parent);
    
            //若“父节点”是“祖父节点的左孩子”
            if (parent == gparent->left) {
                // Case 1条件:叔叔节点是红色
                uncle = gparent->right;
                if (uncle && rb_is_red(uncle)) {
                    rb_set_black(uncle);
                    rb_set_black(parent);
                    rb_set_red(gparent);
                    node = gparent;
                    continue;
                }
    
                // Case 2条件:叔叔是黑色,且当前节点是右孩子
                if (parent->right == node) {
                    _rbtree_left_rotate(tree, parent);
                    uncle = parent;
                    parent = node;
                    node = uncle;
                }
    
                // Case 3条件:叔叔是黑色,且当前节点是左孩子。
                rb_set_black(parent);
                rb_set_red(gparent);
                _rbtree_right_rotate(tree, gparent);
            } 
            else { //若“z的父节点”是“z的祖父节点的右孩子”
                // Case 1条件:叔叔节点是红色
                uncle = gparent->left;
                if (uncle && rb_is_red(uncle)) {
                    rb_set_black(uncle);
                    rb_set_black(parent);
                    rb_set_red(gparent);
                    node = gparent;
                    continue;
                }
    
                // Case 2条件:叔叔是黑色,且当前节点是左孩子
                if (parent->left == node) {
                    _rbtree_right_rotate(tree, parent);
                    uncle = parent;
                    parent = node;
                    node = uncle;
                }
    
                // Case 3条件:叔叔是黑色,且当前节点是右孩子。
                rb_set_black(parent);
                rb_set_red(gparent);
                _rbtree_left_rotate(tree, gparent);
            }
        }
    
        // 将根节点设为黑色
        rb_set_black(tree->root);
    }
    
    /*
     * 插入一个结点, 会插入 new(pack)
     * tree        : 红黑树头结点
     * pack        : 待插入的结点当cmp(x, pack) 右结点
     */
    void 
    rb_insert(rbtree_t tree, void * pack) {
        cmp_f cmp;
        struct rbnode * node, * x, * y;
        if((!tree) || (!pack) || !(node = _rb_new(tree, pack))) {
            fprintf(stderr, "rb_insert param is empty! tree = %p, pack = %p.
    ", tree, pack);
            return;    
        }
        
        cmp = tree->cmp;
        // 开始走插入工作
        y = NULL;
        x = tree->root;
    
        // 1. 将红黑树当作一颗二叉查找树,将节点添加到二叉查找树中。从小到大
        while (x != NULL) {
            y = x;
            if (cmp(x, node) > 0)
                x = x->left;
            else
                x = x->right;
        }
        rb_set_parent(node, y);
    
        if (y != NULL) {
            if (cmp(y, node) > 0)
                y->left = node;             // 情况2:若“node所包含的值” < “y所包含的值”,则将node设为“y的左孩子”
            else
                y->right = node;            // 情况3:(“node所包含的值” >= “y所包含的值”)将node设为“y的右孩子” 
        }
        else
            tree->root = node;              // 情况1:若y是空节点,则将node设为根
    
        // 2. 设置节点的颜色为红色
        rb_set_red(node);
    
        // 3. 将它重新修正为一颗二叉查找树
        _rbtree_insert_fixup(tree, node);
    }
    
    /*
     * 红黑树删除修正函数
     *
     * 在从红黑树中删除插入节点之后(红黑树失去平衡),再调用该函数;
     * 目的是将它重新塑造成一颗红黑树。
     *
     * 参数说明:
     *     tree 红黑树的根
     *     node 待修正的节点
     */
    static void _rbtree_delete_fixup(rbtree_t tree, struct rbnode * node, struct rbnode * parent) {
        struct rbnode * other;
    
        while ((!node || rb_is_black(node)) && node != tree->root) {
            if (parent->left == node) {
                other = parent->right;
                if (rb_is_red(other)) {
                    // Case 1: x的兄弟w是红色的  
                    rb_set_black(other);
                    rb_set_red(parent);
                    _rbtree_left_rotate(tree, parent);
                    other = parent->right;
                }
                if ((!other->left || rb_is_black(other->left)) &&
                    (!other->right || rb_is_black(other->right))) {
                    // Case 2: x的兄弟w是黑色,且w的俩个孩子也都是黑色的  
                    rb_set_red(other);
                    node = parent;
                    parent = rb_parent(node);
                }
                else {
                    if (!other->right || rb_is_black(other->right)) {
                        // Case 3: x的兄弟w是黑色的,并且w的左孩子是红色,右孩子为黑色。  
                        rb_set_black(other->left);
                        rb_set_red(other);
                        _rbtree_right_rotate(tree, other);
                        other = parent->right;
                    }
                    // Case 4: x的兄弟w是黑色的;并且w的右孩子是红色的,左孩子任意颜色。
                    rb_set_color(other, rb_color(parent));
                    rb_set_black(parent);
                    rb_set_black(other->right);
                    _rbtree_left_rotate(tree, parent);
                    node = tree->root;
                    break;
                }
            }
            else {
                other = parent->left;
                if (rb_is_red(other)) {
                    // Case 1: x的兄弟w是红色的  
                    rb_set_black(other);
                    rb_set_red(parent);
                    _rbtree_right_rotate(tree, parent);
                    other = parent->left;
                }
                if ((!other->left || rb_is_black(other->left)) &&
                    (!other->right || rb_is_black(other->right))) {
                    // Case 2: x的兄弟w是黑色,且w的俩个孩子也都是黑色的  
                    rb_set_red(other);
                    node = parent;
                    parent = rb_parent(node);
                }
                else {
                    if (!other->left || rb_is_black(other->left)) {
                        // Case 3: x的兄弟w是黑色的,并且w的左孩子是红色,右孩子为黑色。  
                        rb_set_black(other->right);
                        rb_set_red(other);
                        _rbtree_left_rotate(tree, other);
                        other = parent->left;
                    }
                    // Case 4: x的兄弟w是黑色的;并且w的右孩子是红色的,左孩子任意颜色。
                    rb_set_color(other, rb_color(parent));
                    rb_set_black(parent);
                    rb_set_black(other->left);
                    _rbtree_right_rotate(tree, parent);
                    node = tree->root;
                    break;
                }
            }
        }
        if (node)
            rb_set_black(node);
    }
    
    /*
     * 删除rb_get得到的结点
     * root        : 红黑树结点
     * pack        : 当cmp(x, pack) 右结点
     */
    void 
    rb_remove(rbtree_t tree, void * pack) {
        struct rbnode * child, * parent, * node = NULL;
        int color;
        
        if ((!tree) || !(node = (struct rbnode *)pack)) {
            fprintf(stderr, "rb_remove check is error, tree = %p, node = %p.", tree, node);
            return;
        }
    
        // 被删除节点的"左右孩子都不为空"的情况。
        if (NULL != node->left && node->right != NULL) {
            // 被删节点的后继节点。(称为"取代节点")
            // 用它来取代"被删节点"的位置,然后再将"被删节点"去掉。
            struct rbnode * replace = node;
    
            // 获取后继节点
            replace = replace->right;
            while (replace->left != NULL)
                replace = replace->left;
    
            // "node节点"不是根节点(只有根节点不存在父节点)
            if ((parent = rb_parent(node))) {
                if (parent->left == node)
                    parent->left = replace;
                else
                    parent->right = replace;
            } 
            else 
                // "node节点"是根节点,更新根节点。
                tree->root = replace;
    
            // child是"取代节点"的右孩子,也是需要"调整的节点"。
            // "取代节点"肯定不存在左孩子!因为它是一个后继节点。
            child = replace->right;
            parent = rb_parent(replace);
            // 保存"取代节点"的颜色
            color = rb_color(replace);
    
            // "被删除节点"是"它的后继节点的父节点"
            if (parent == node)
                parent = replace; 
            else {
                // child不为空
                if (child)
                    rb_set_parent(child, parent);
                parent->left = child;
    
                replace->right = node->right;
                rb_set_parent(node->right, replace);
            }
            
            rb_set_parent(replace, rb_parent(node));
            rb_set_color(replace, rb_color(node));
            replace->left = node->left;
            rb_set_parent(node->left, replace);
    
            if (color) // 黑色结点重新调整关系
                _rbtree_delete_fixup(tree, child, parent);
            // 结点销毁操作
            if(tree->die)
                tree->die(node);
            return ;
        }
    
        if (node->left !=NULL)
            child = node->left;
        else 
            child = node->right;
    
        parent = rb_parent(node);
        // 保存"取代节点"的颜色
        color = rb_color(node);
    
        if (child)
            rb_set_parent(child, parent);
    
        // "node节点"不是根节点
        if (parent) {
            if (parent->left == node)
                parent->left = child;
            else
                parent->right = child;
        }
        else
            tree->root = child;
    
        if (!color)
            _rbtree_delete_fixup(tree, child, parent);
        if(tree->die)
            tree->die(node);
    }
    
    /*
     * 得到红黑树中匹配的结点
     * root        : 匹配的结点信息
     * pack        : 当前待匹配结点, cmp(x, pack)当右结点处理
     */
    void * 
    rb_get(rbtree_t tree, void * pack) {
        cmp_f cmp;
        struct rbnode * node;
        if((!tree) || !pack) {
            fprintf(stderr, "rb_get param is empty! tree = %p, pack = %p.
    ", tree, pack);
            return NULL;    
        }
        
        cmp = tree->cmp;
        node = tree->root;
        while(node) {
            int ct = cmp(node, pack);
            if(ct == 0)
                return node;
            node = ct > 0 ? node->left : node->right;
        }
    
        return NULL;
    }
    
    // 后序遍历删除操作
    static void _rb_die(struct rbnode * root, die_f die) {
        if(NULL == root)
            return;
        _rb_die(root->left, die);
        _rb_die(root->right, die);
        die(root);
    }
    
    /*
     * 销毁这颗二叉树
     * root        : 当前红黑树结点
     */
    void
    rb_die(rbtree_t tree) {
        if(!tree || !tree->root || !tree->die)
            return;
    
        // 后续递归删除
        _rb_die(tree->root, tree->die);
    
        // 销毁树本身内存
        tree->root = NULL;
        free(tree);
    }
    View Code

    上面代码主要基于linux内核中红黑树扒下来构建的工程库. 有些细节我们简单解释一下结构.  例如

    /*
     * 每个想使用红黑树的结构, 需要在头部插入下面宏. 
     * 例如 :
        struct person {
            _HEAD_RBTREE;
            ... // 自定义信息
        };
     */
    #define _HEAD_RBTREE    struct rbnode __node

    等同于'继承'用法, 放在没一个希望用在红黑树结构的头部. 这些都是从linux内核结构中学到的技巧. libuv框架中也常用这种技巧.
    也是C开发中通用潜规则! 还有一个技巧, 如下

    struct rbnode {
        unsigned long    parent_color;
        struct rbnode * right;
        struct rbnode * left;
    };
    
    #define rb_parent(r)        ((struct rbnode *)((r)->parent_color & ~3))
    #define rb_color(r)        ((r)->parent_color & 1)

    也是在看内核源码中学到的技巧, 将指针的后2位地址, 用于保存结点颜色. 为什么可行呢,

    因为 struct rbnode 结构体内存是以 sizeof (unsigned long) 大小对齐. 那么该结构地址也是以 n*sizeof(unsigned long) 递增.

    后两位都是0空出来的. 用于保存红黑树结点的颜色信息(RED | BLACK). 不得不佩服linux内核代码的精巧.

    后面还有一个自己补充的技巧

    typedef void * (* new_f)(void *);
    typedef int (* cmp_f)(const void *, const void *);
    typedef void (* die_f)(void *);
    
    typedef struct {
        struct rbnode * root;
        new_f new;
        cmp_f cmp;
        die_f die;
    } * rbtree_t;

    实现注册, 创建, 比较, 销毁行为函数, 方便使用. 采用匿名结构, 也是一个C中开发一个小技巧, 这个结构只能是堆上创建. 对外可见, 但是不可构建.

    后面会基于这个红黑树基础库, 构建一个简繁对照字典. 最后重申一下, 红黑树是软件开发层最后的堡垒. 数据结构算法也就到这了.

     

    正文  -- 简单分析设计和测试

       C的设计, 主要看结构. 同样C的难点也是结构. 后面我们做一个简单的简繁转换的字典, 通过C.

    需要的资源见这个文件  http://files.cnblogs.com/files/life2refuel/C%E9%AB%98%E7%BA%A7%E5%B7%A5%E7%A8%8B%E4%B8%AD%E4%BD%BF%E7%94%A8%E7%BA%A2%E9%BB%91%E6%A0%91%E5%9F%BA%E5%BA%93.zip

    简繁变换的词典,window上截图如下

    采用的是ascii编码, 这里一个汉字2字节表示. 上传到linux上后, 采用utf-8编码, 一个中文3个字节. 需要小心!

    词典主程序 main.c

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include "rbtree.h"
    
    #define _STR_PATH    "常用汉字简繁对照表.txt"
    
    #define _INT_DICT    (4)
    
    struct dict {
        _HEAD_RBTREE;
    
        char key[_INT_DICT];
        char value[_INT_DICT];
    };
    
    // 需要注册的内容
    static void * _dict_new(void * arg) {
        struct dict * node = malloc(sizeof(struct dict));
        if (NULL == node) {
            fprintf(stderr, "_dict_new malloc is error!
    ");
            return NULL;
        }
    
        *node = *(struct dict *)arg;
        return node;
    }
    
    static inline int _dict_cmp(const void * ln , const void * rn) {
        return strcmp(((const struct dict *)ln)->key, ((const struct dict *)rn)->key);
    }
    
    static inline void _dict_die(void * arg) {
        free(arg);
    }
    
    // 创建内容
    void dict_create(rbtree_t tree);
    // 得到内容
    const char * dict_get(rbtree_t tree, const char * key);
    
    /*
     * 这里测试字典数据, 通过红黑树库
     */
    int main(int argc, char * argv[]) {
        // 创建字典树, 再读取内容
        rbtree_t tree = rb_new(_dict_new, _dict_cmp, _dict_die);
        if (NULL == tree) {
            fprintf(stderr, "main rb_new rb is error!
    ");
            return -1;
        }
    
        // 为tree填充字典数据
        dict_create(tree);
    
        // 我们输出一下 '你好'
        printf("你好吗 -> %s%s%s
    ", 
            dict_get(tree, ""), 
            dict_get(tree, ""),
            dict_get(tree, "")
        );
    
        // 字典书删除
        rb_die(tree);
    
        getchar();
        return 0;
    }
    
    // 创建内容
    void 
    dict_create(rbtree_t tree) {
        char c;
        struct dict kv;
        // 打开文件内容
        FILE * txt = fopen(_STR_PATH, "rb");
        if (NULL == txt) {
            fprintf(stderr, "main fopen " _STR_PATH " rb is error!
    ");
            return;
        }
        
        while ((c = fgetc(txt))!=EOF) {
            memset(&kv, 0, sizeof kv);
            // 读取这一行key, 并设值
            kv.key[0] = c;
            kv.key[1] = fgetc(txt);
    
            // 去掉\t
            c = fgetc(txt);
            if(c < 0) {
                kv.key[2] = c;
                fgetc(txt);
            }
    
            // 再设置value
            kv.value[0] = fgetc(txt);
            kv.value[1] = fgetc(txt);
            
            c = fgetc(txt);
            if (c != ' ') {// 这些SB的代码, 都是解决不同系统版本的编码冲突的
                kv.value[2] = c;
                fgetc(txt);
            }
    // 去掉 fgetc(txt); // 插入数据 rb_insert(tree, &kv); } // 合法读取内容部分 fclose(txt); } // 得到内容 const char * dict_get(rbtree_t tree, const char * key) { struct dict kv; strncpy(kv.key, key, sizeof(kv.key) / sizeof(char)); struct dict * pkv = rb_get(tree, &kv); return pkv ? pkv->value : NULL; }

    先看 window上测试结果

    上面关于  dict_create 关于配置文件解析, 采用最原始的编码字符数解析的.

    linux上 测试过程如下

     

    linux上测试结果很正常. 到这里, 红黑树基库demo演示完毕. 也许你觉得好复杂, 但是已经很简单了. 因为C程序一个要求就是,

    你需要懂得实现. 才能运用流畅. 一切都是钻木取火, 自生自灭.

    扯一点, C要是有那种万能数据结构 array 或者 table 那生产率预估会提升10倍. 写代码就和玩似的. 

    今天完工等价于C基础数据结构已经全线通工了. C的代码写的越多, 越发觉得喜欢就好!

    后记  -- 一些客气话

      错误是难免的, 欢迎指正交流提高. 

         回家   http://music.163.com/#/song?id=157336

      

  • 相关阅读:
    原代码,反码,解释和具体的补充 Java在&gt;&gt;和&gt;&gt;&gt;差异
    开源 自由 java CMS
    Socket方法LAN多线程文件传输
    《》猿从程序书评项目经理-猿自办节目
    今年,我开始在路上
    mysql 拒绝访问的解决办法
    Mysql连接错误:Mysql Host is blocked because of many connection errors
    基于jquery的从一个页面跳转到另一个页面的指定位置的实现代码
    【转】URL编码(encodeURIComponent和decodeURIComponent)
    oracle sql日期比较
  • 原文地址:https://www.cnblogs.com/life2refuel/p/5826224.html
Copyright © 2011-2022 走看看