引言  — 红黑树历史

引言  — 红黑树历史

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

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

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

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

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

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

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

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

  红黑树(英语:Red–black
tree)是一种起平衡二叉查找树,是在电脑是倍受之所以到的相同种植数据结构,典型的用途是落实事关数组。它是在1972年由鲁道夫·贝尔表的,他叫”对如二叉B树”,它现代之名是以Leo
J. Guibas和Robert
Sedgewick为1978年写的等同首论文被得到的。它是复杂的,但它们的操作有美好的极度可怜情况运行时,并且在实践中是快速的:它可以O(log
n)时间外举行查找,插入和去,这里的n凡培训中元素的多少。

  红黑树(英语: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

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

正文重点介绍工程支出被, 红黑树工程基库的封装.直接用现成的极其爽.

正文重点介绍工程开发被, 红黑树工程基库的封装.直接用现成的卓绝爽.

 

 

序言  — 红黑树工程库源码

前言  — 红黑树工程库源码

   一言不合就达到源码! 

   一言不合就达成源码! 

rbtree.h

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 */
#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

rbtree.c

365bet体育投注 1365bet体育投注 2

365bet体育投注 3365bet体育投注 4

#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.\n", 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.\n", 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);
}
#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.\n", 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.\n", 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

View Code

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

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

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

一如既往于’继承’用法, 放在没一个意在就此当红黑树结构的头部.
这些还是打linux内核结构中学至之技巧. libuv框架中为常常因此这种技巧.
否是C开发中通用潜规则! 还有一个术, 如下

一律于’继承’用法, 放在没一个望用在红黑树结构的头部.
这些还是起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)
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号地方, 用于保存结点颜色.
为什么可行呢,

否是以羁押内核源码中学至之技艺, 将指针的晚2个地方, 用于保存结点颜色.
为什么可行呢,

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

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

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

继少个还是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;
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中开发一个不怎么技巧, 这个组织只能是堆上创建. 对外可见,
但是免可构建.

末端会冲这红黑树基础库, 构建一个简繁对照字典. 最后重复一下,
红黑树是软件开发层最后之堡垒. 数据结构算法为即顶当下了.

末尾会冲这红黑树基础库, 构建一个简繁对照字典. 最后重复一下,
红黑树是软件开发层最后之堡垒. 数据结构算法也尽管顶立刻了.

 365bet体育投注 5

 365bet体育投注 6

 

 

恰文  — 简单解析规划与测试

刚文  — 简单分析规划以及测试

   C的计划, 主要看结构. 同样C的难关也是结构.
后面我们开一个略的简繁转换的字典, 通过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

用的资源呈现这个文件 
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上截图如下

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

365bet体育投注 7

365bet体育投注 8

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

行使的是ascii编码, 这里一个中国字2配节表示. 上传到linux上后,
采用utf-8编码, 一个华语3独字节. 需要小心!

词典主程序 main.c

词典主程序 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!\n");
        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!\n");
        return -1;
    }

    // 为tree填充字典数据
    dict_create(tree);

    // 我们输出一下 '你好'
    printf("你好吗 -> %s%s%s\n", 
        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!\n");
        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 != '\r') {// 这些SB的代码, 都是解决不同系统版本的编码冲突的
            kv.value[2] = c;
            fgetc(txt);
        }

        // 去掉\n
        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;
}
#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!\n");
        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!\n");
        return -1;
    }

    // 为tree填充字典数据
    dict_create(tree);

    // 我们输出一下 '你好'
    printf("你好吗 -> %s%s%s\n", 
        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!\n");
        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 != '\r') {// 这些SB的代码, 都是解决不同系统版本的编码冲突的
            kv.value[2] = c;
            fgetc(txt);
        }

        // 去掉\n
        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上测试结果

先期押 window上测试结果

365bet体育投注 9

365bet体育投注 10

方关于  dict_create
关于配置文件分析, 采用最老的编码字符数解析的.

地方关于  dict_create
关于配置文件分析, 采用最原始之编码字符数解析的.

linux上 测试过程如下

linux上 测试过程如下

365bet体育投注 11

365bet体育投注 12

 365bet体育投注 13

 365bet体育投注 14

linux上测试结果十分正常. 到此处, 红黑树基库demo演示了毕. 也许你道好复杂,
但是早就生简短了. 因为C程序一个求就是,

linux上测试结果十分正常. 到此处, 红黑树基库demo演示了毕. 也许你道好复杂,
但是都特别简短了. 因为C程序一个渴求就是,

卿得了解实现. 才能够应用流畅. 一切都是钻木取火, 自生自灭.

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

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

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

今日完工等价于C基础数据结构已经全线通工了. C的代码写的尤其多,
越发认为好就哼!

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

 

 

晚记  — 一些客气话

晚记  — 一些客气话

  错误是免不了的, 欢迎指正交流增进. 

  错误是难免的, 欢迎指正交流加强. 

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

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

365bet体育投注 15

365bet体育投注 16

  

  

相关文章

网站地图xml地图