rb_tree红黑树

1、概念:

标准库的std::map、std::mulitmap、std::set、 std::multiset统称为关联式容器,其底层数据结构是基于红黑树实现的。

红黑树作为一种二叉树,满足以下规则:

  1)每个节点不是红色就是黑色

  2)根节点为黑色

  3)如果节点为红色,其子节点必须为黑色

  4)任何一节点至树尾的任何路径,所含的黑节点树必须相同

如果不满足以上规则,就需要调整颜色并旋转树形

2、红黑树的实现

  2.1 红黑树节点

  红黑树的节点包含5个属性:自身颜色(红或黑)、指向父节点的指针、指向左子节点的指针、指向右子节点的指针,加上自身存储的数据,

其代码实现如下:

enum rb_tree_color { s_red = false, s_black = true };
// 红黑树节点
template <typename Value>
struct Rb_tree_node
{
    typedef rb_tree_color color_type;
    typedef Rb_tree_node<Value>* link_type;

    // 一直向左,可找到最小节点
    static link_type minimum(link_type x)
    {
        while (x->m_left != 0)
        {
            x = x->m_left;
        }
        return x;
    }

    // 一直向右,可找到最大节点
    static link_type maximum(link_type x)
    {
        while (x->m_right != 0)
        {
            x = x->m_right;
        }
        return x;
    }
    Value m_value_field;  //节点值
    color_type m_color; // 节点颜色
    link_type m_parent;  // 父节点
    link_type m_left;    // 左子节点
    link_type m_right;    // 右子节点
};

    2.2 迭代器实现 

    迭代器的递增操作分四种情况:

  1) 如果有右子节点,如 节点15 递增,需要先右,再一直往左操作,节点20就是所求

  2) 如果没有右子节点

    a. 例如节点25、节点70递增,先找父节点,只要自身是父节点的右子节点,则一直向上,直到不是右子节点,此时父节点就是所求

    b. 例如节点5,节点10递增,父节点就是所求

    c. 存在特殊情况,如图2所示,根节点30递增,在a、b逻辑基础上需要加上特殊判断。

 

  迭代器的递减操作分三种情况:

  1)如果对header递减,指向其右子节点,即整棵树的max节点

  2)如果有左子节点,则先左再一直往右,例如节点75

  3)如果没有左子节点,先找父节点,如果自身是父节点的左子节点,则一直向上,直到不是左子节点,即为所求,例如节点20

迭代器的代码实现:

// 红黑树迭代器
template <typename Value>
struct Rb_tree_iterator
{
    typedef Value value_type;
    typedef Value& reference;
    typedef Value* pointer;

    typedef Rb_tree_iterator<Value> self;
    typedef Rb_tree_node<Value>* link_type;

    Rb_tree_iterator() :m_node(nullptr) {}
    Rb_tree_iterator(link_type x) { m_node = x; }

    // 递增操作
    void increment()
    {
        if (m_node->m_right != 0)
        {
            m_node = m_node->m_right;            // 1)如果有右子节点,先右后左到底就是
            while (m_node->m_left != 0)
            {
                m_node = m_node->m_left;
            }
        }
        else
        {
            link_type y = m_node->m_parent;      // 2) 没有右子节点,先找父节点
            while (m_node == y->m_right)
            {  // 一直向上,直到不是右子节点位置
                m_node = y;
                y = y->m_parent;
            }
            if (m_node->m_right != y)          // 排除特殊情况,根节点无右子节点,对根节点递增
            {
                m_node = y;                       //包含a、b、c三种情况,
            }
        }
    }
    // 递减操作
    void decrement()
    {
        if (m_node->m_color == s_red &&                // 1)如果node 为 header, 其右子节点为mostright, 指向整棵树的max节点 
            m_node->m_parent->m_parent == m_node)   // 该节点是红节点,且父节点的父节点等于自己,右子节点就是
        {
            m_node = m_node->m_right;

        }
        else if (m_node->m_left != 0)            // 2) 如果有左子节点,先左后右,右到底就是
        {
            link_type y = m_node->m_left;
            while (y->m_right != 0)
            {
                y = y->m_right;
            }
            m_node = y;
        }
        else                                        // 3)如果没有左子节点,找父节点,如果是左子节点,一直向上,直到不是
        {                                            // 左子节点
            link_type y = m_node->m_parent;
            while (m_node == y->m_left)
            {
                m_node = y;
                y = y->m_parent;
            }
            m_node = y;
        }
    }

    // 重载操作符
    reference operator*() const
    {
        return static_cast<link_type>(m_node)->m_value_field;
    }
    pointer operator->() const { return &(operator*()); }
    self& operator++() { increment(); return *this; }
    self operator++(int)
    {
        self tmp = *this;
        increment();
        return tmp;
    }
    self& operator--() { decrement(); return *this; }
    self operator--(int)
    {
        self tmp = *this;
        decrement();
        return tmp;
    }
    bool operator==(const self& x) const
    {
        return m_node == x.m_node;
    }

    bool operator!=(const self& x) const
    {
        return m_node != x.m_node;
    }
    link_type m_node;
};

  2.3 红黑树自平衡操作

    情况1:父节点为黑色,不用操作

 

 

     情况2:父节点为红

      1) 伯父节点为红

 

 

 

       2)伯为空或黑

        a、父节点是祖父节点的左子节点

          a1: 新增节点为父节点的右子节点

          a2: 新增节点为父节点的左子节点

    

        b、新增节点为父节点的左子节点

          b1: 新增节点为父节点的左子节点

 

           b2: 新增节点为父节点的右子节点

 

 

 代码实现:

// 全局函数,向左旋转
// param1_in: x 旋转节点
// param2_out: root 根节点
template<typename Value>
inline void rb_tree_rotate_left(Rb_tree_node<Value>* x, Rb_tree_node<Value>*& root)
{// 一共更改了三对指针的指向
    Rb_tree_node<Value>* y = x->m_right;    // x绕着其右子节点y旋转
    x->m_right = y->m_left;                // x的右子节点指向y的左子节点
    if (y->m_left != 0)
    {
        y->m_left->m_parent = x;        // y的左子节点的父节点指向x
    }
    y->m_parent = x->m_parent;            // y的父节点指向x的父节点

    if (x == root)                        // 如果是根节点,指明y为根节点
    {
        root = y;
    }
    else if (x == x->m_parent->m_left)
    {
        x->m_parent->m_left = y;        // x的父节点原先指向x,现改为指向y
    }
    else
    {
        x->m_parent->m_right = y;        // x的父节点原先指向x,现改为指向y
    }
    y->m_left = x;                        // y的左节点指向x
    x->m_parent = y;                    // x的父节点指向y
}
// 全局函数,向右旋转 // param1_in: x 旋转节点 // param2_out: root 根节点 template<typename Value> inline void rb_tree_rotate_right(Rb_tree_node<Value>* x, Rb_tree_node<Value>*& root) { // 一共更改了三对指针的指向 Rb_tree_node<Value>* y = x->m_left; // x绕着其左子节点y旋转 x->m_left = y->m_right; // x的左子节点指向y的右子节点 if (y->m_right != 0) { y->m_right->m_parent = x; // y的右子节点的父节点指向x } y->m_parent = x->m_parent; // y的父节点指向x的父节点 if (x == root) { root = y; //如果是根节点,指明y为根节点 } else if (x == x->m_parent->m_right) { x->m_parent->m_right = y; // x的父节点原先指向x,现改为指向y } else { x->m_parent->m_left = y; // x的父节点原先指向x,现改为指向y } y->m_right = x; // y的右节点指向x x->m_parent = y; // x的父节点指向y } // 全局函数,插入新节点后令树形平衡 // param1_in :x 新增节点 // param2_out: root 根节点 template<typename Value> inline void rb_tree_rebalance(Rb_tree_node<Value>* x, Rb_tree_node<Value>*& root) { x->m_color = s_red; // 新增节点要为红节点 while (x != root && x->m_parent->m_color == s_red) //父节点为红 { if (x->m_parent == x->m_parent->m_parent->m_left) // 父节点为祖父节点的左子节点 { Rb_tree_node<Value>* y = x->m_parent->m_parent->m_right; // 令y为伯父节点,即父父节点的右子节点 if (y && y->m_color == s_red) // 伯父节点存在,且为红色节点 { x->m_parent->m_color = s_black; // 改父节点为黑 y->m_color = s_black; // 改伯父节点为黑 x->m_parent->m_parent->m_color = s_red; // 更改祖父节点为红 x = x->m_parent->m_parent; // 向上至祖父节点迭代 } else { // 无伯父节点或伯父节点为黑 if (x == x->m_parent->m_right) // 如果新节点为父节点的右子节点 { x = x->m_parent; rb_tree_rotate_left(x, root); // 对x父节点左旋转 } x->m_parent->m_color = s_black; // 改变颜色 x->m_parent->m_parent->m_color = s_red; rb_tree_rotate_right(x->m_parent->m_parent, root); // 对祖父节点右旋转 } } else { // 父节点为祖父节点的右子节点 Rb_tree_node<Value>* y = x->m_parent->m_parent->m_left;// 令y为伯父节点,即父父节点的左子节点 if (y && y->m_color == s_red) // 伯父节点存在,且为红色节点 { x->m_parent->m_color = s_black; // 改父节点为黑 y->m_color = s_black; // 改伯父节点为黑 x->m_parent->m_parent->m_color = s_red; // 更改祖父节点为红 x = x->m_parent->m_parent; // 向上至祖父节点迭代 } else { // 无伯父节点或伯父节点为黑 if (x == x->m_parent->m_left) // 如果新节点为父节点的左子节点 { x = x->m_parent; rb_tree_rotate_right(x, root); // 对x父节点右旋转 } x->m_parent->m_color = s_black; // 改变颜色 x->m_parent->m_parent->m_color = s_red; rb_tree_rotate_left(x->m_parent->m_parent, root);// 对祖父节点左旋转 } } } root->m_color = s_black; // 根节点永远为黑 } //全局函数,删除节点后令树形平衡 //z-待删除点,root-根节点,leftmost-左极点,rightmost-右极点 template<typename Value> inline Rb_tree_node<Value>* rb_tree_rebalance_for_erase(Rb_tree_node<Value>* z, Rb_tree_node<Value>*& root, Rb_tree_node<Value>*& leftmost, Rb_tree_node<Value>*& rightmost) { Rb_tree_node<Value>* y = z; // 调整后实际被删除点的指针 Rb_tree_node<Value>* x = 0; // y的某个子节点 Rb_tree_node<Value>* x_parent = 0; // x的父节点 if (y->m_left == 0) { x = y->m_right; // 这里x可能为null } else { if (y->m_right == 0) { x = y->m_left; // 这里x肯定不为null } else { // z 有两个非空子节点,设定y为z的后继,x为y的右节点,x可能为空 y = y->m_right; while (y->m_left != 0) { y = y->m_left; } x = y->m_right; } } if (y != z) // 重新设置y的连接以取代z,y是z的后继 { z->m_left->m_parent = y; y->m_left = z->m_left; if (y != z->m_right) { x_parent = y->m_parent; if (x) { x->m_parent = y->m_parent; } y->m_parent->m_left = x; // y一定是其父节点的左孩子 y->m_right = z->m_right; z->m_right->m_parent = y; } else { x_parent = y; } if (root == z) { root = y; } else if (z->m_parent->m_left == z) { z->m_parent->m_left = y; } else { z->m_parent->m_right = y; } y->m_parent = z->m_parent; std::swap(y->m_color, z->m_color); y = z; // y 现在指向实际被删除的节点 } else { // y == z x_parent = y->m_parent; if (x) { x->m_parent = y->m_parent; } if (root == z) { root = x; } else { if (z->m_parent->m_left == z) { z->m_parent->m_left = x; } else { z->m_parent->m_right = x; } } if (leftmost == z) { if (z->m_right == 0) // z->m_left也一定为0 {// 设定leftmost为z的父节点,覆盖了z为root,leftmost指向m_header的情况 leftmost = z->m_parent; } else { leftmost = Rb_tree_node<Value>::minimum(x); } } if (rightmost == z) { if (z->m_left == 0) // z->m_right也一定为0 {// 设定rightmost为z的父节点,覆盖了z为root,rightmost指向m_header的情况 rightmost = z->m_parent; } else { rightmost = Rb_tree_node<Value>::maximum(x); } } } if (y->m_color != s_red) { while (x != root && (x == 0 || x->m_color == s_black)) { if (x == x_parent->m_left) { Rb_tree_node<Value>* w = x_parent->m_right; if (w->m_color == s_red) { w->m_color = s_black; x_parent->m_color = s_red; rb_tree_rotate_left(x_parent, root); w = x_parent->m_right; } if ((w->m_left == 0 || w->m_left->m_color == s_black) && (w->m_right == 0 || w->m_right->m_color == s_black)) { w->m_color = s_red; x = x_parent; x_parent = x_parent->m_parent; } else { if (w->m_right == 0 || w->m_right->m_color == s_black) { if (w->m_left) { w->m_left->m_color = s_black; } w->m_color = s_red; rb_tree_rotate_right(w, root); w = x_parent->m_right; } w->m_color = x_parent->m_color; x_parent->m_color = s_black; if (w->m_right) { w->m_right->m_color = s_black; } rb_tree_rotate_left(x_parent, root); break; } } else { Rb_tree_node<Value>* w = x_parent->m_left; if (w->m_color == s_red) { w->m_color = s_black; x_parent->m_color = s_red; rb_tree_rotate_right(x_parent, root); w = x_parent->m_left; } if ((w->m_right == 0 || w->m_right->m_color == s_black) && (w->m_left == 0 || w->m_left->m_color == s_black)) { w->m_color = s_red; x = x_parent; x_parent = x_parent->m_parent; } else { if (w->m_left == 0 || w->m_left->m_color == s_black) { if (w->m_right) { w->m_right->m_color = s_black; } w->m_color = s_red; rb_tree_rotate_left(w, root); w = x_parent->m_left; } w->m_color = x_parent->m_color; x_parent->m_color = s_black; if (w->m_left) { w->m_left->m_color = s_black; } rb_tree_rotate_right(x_parent, root); break; } } } if (x) { x->m_color = s_black; } } return y; }

3、 rb_tree模板类的代码实现

//rb-tree定义
template <typename Key, typename Value, typename KeyOfValue, typename Compare, typename Alloc = std::allocator<Value>>
class Rb_tree
{
protected:
    typedef Rb_tree_node<Value> rb_tree_node;
public:
    typedef Key key_type;
    typedef Value value_type;
    typedef rb_tree_node* link_type;
    typedef size_t size_type;
    std::allocator<rb_tree_node> m_alloc;
protected:
    // 申请节点空间
    link_type get_node()
    {
        return m_alloc.allocate(1);  // 申请内存就简单使用std::allocator,主要是验证红黑树
    }
    // 释放节点空间
    void put_node(link_type p)
    {
        m_alloc.deallocate(p, 1); // 申请内存就简单使用std::allocator,主要是验证红黑树
    }
    // 创建一个节点
    link_type create_node(const value_type& x)
    {
        link_type tmp = get_node();
        rb_tree_node node;
        node.m_value_field = x;
        m_alloc.construct(tmp, node);
        return tmp;
    }
    // 复制一个节点
    link_type clone_node(link_type x)
    {
        link_type tmp = create_node(x->m_value_field);
        tmp->m_color = x->m_color;
        tmp->m_left = 0;
        tmp->m_right = 0;
        return tmp;
    }
    // 释放节点,先析构后释放内存
    void destroy_node(link_type p)
    {
        m_alloc.destroy(p);
        put_node(p);
    }
protected:
    size_type m_node_count; // 数的节点数量
    link_type m_header;      // header 节点
    Compare   m_key_compare;// 节点间的键值大小比较准则
    KeyOfValue m_keyOfValue;

    // 求取极大值和极小值 node class 有实现此功能,交给它们完成即可
    static link_type minimum(link_type x)
    {
        return (link_type)Rb_tree_node::minimum(x);
    }
    static link_type maximum(link_type x)
    {
        return (link_type)Rb_tree_node::maximum(x);
    }

public:
    // 定义迭代器
    typedef Rb_tree_iterator<value_type> iterator;

private:
    iterator m_insert(link_type x, link_type y, const value_type& v)
    {
        link_type x1 = (link_type)x;
        link_type y1 = (link_type)y;
        link_type z1;

        if (y1 == m_header || x1 != 0 || m_key_compare(KeyOfValue()(v), m_keyOfValue(y1->m_value_field)))
        {
            z1 = create_node(v);            // 创建一个新节点
            y1->m_left = z1;                    // 这样当y为header时,z1为leftmost()
            if (y1 == m_header)
            {
                m_header->m_parent = z1;
                m_header->m_right = z1;
            }
            else if (y1 == m_header->m_left)
            {
                m_header->m_left = z1;   // 保证rightmost()指向最左点
            }
        }
        else {
            z1 = create_node(v);  // 创建新节点
            y1->m_right = z1;       // 新节点是y的右子节点
            if (y1 == m_header->m_right)
            {
                m_header->m_right = z1;  // 保证rightmost()指向最右点
            }
        }
        z1->m_parent = y1;        // 设定新节点的父节点
        z1->m_left = 0;            //    设定左子节点
        z1->m_right = 0;            //    设定右子节点
        rb_tree_rebalance(z1, m_header->m_parent); //平衡树调节,参数1为新增节点,参数2 为根节点
        ++m_node_count;            // 节点数累加
        return iterator(z1);    // 返回指向新增节点的迭代器
    }
    link_type m_copy(link_type x, link_type p)
    {
        link_type top = clone_node(x);
        top->m_parent = p;

        if (x->m_right)
        {
            top->m_right = m_copy(x->m_right, top);
        }
        p = top;
        x = x->m_left;

        while (x != 0)
        {
            link_type y = clone_node(x);
            p->m_left = y;
            y->m_parent = p;
            if (x->m_right)
            {
                y->m_right = copy(x->m_right, y);
            }
            p = y;
            x = x->m_left;
        }
        return top;
    }
    void initialize()
    {
        m_header = get_node(); // 产生一个节点空间,令header指向它
        m_header->m_color = s_red; // 令header为红色,用来区分header和root
        m_header->m_parent = 0;
        m_header->m_left = m_header;    // header 的左子节点为自己
        m_header->m_right = m_header; // 令header的右子节点为自己
    }
    // 找到第一个大于或等于k的迭代器
    iterator lower_bound(const Key& k)
    {
        link_type y = m_header;
        link_type x = m_header->m_parent; 

        while (x != 0)
        { 
            if (!m_key_compare(m_keyOfValue(x->m_value_field), k))
            {
                y = x;
                x = x->m_left;
            }
            else
            {
                x = x->m_right;
            }
        }
        return iterator(y);
    }
    // 找到第一个大于k的迭代器
    iterator upper_bound(const Key& k)
    {
        link_type y = m_header; 
        link_type x = m_header->m_parent;

        while (x != 0)
        {
            if (m_key_compare(k, m_keyOfValue(x->m_value_field)))
            {
                y = x;
                x = x->m_left;

            }
            else
            {
                x = x->m_right;
            }
        }
        return iterator(y);
    }
    std::pair<iterator,iterator> equal_range(const Key& k)
    {
        return std::pair<iterator, iterator>(lower_bound(k), upper_bound(k));
    }

    void m_erase(link_type x)
    {
        while (x != 0) 
        {
            m_erase(x->m_right);
            link_type y = x->m_left;
            destroy_node(x);
            x = y;
        }
    }
public:
    // 构造函数
    Rb_tree() : m_node_count(0)
    {
        initialize();
    }
    Rb_tree(const Rb_tree& x) : m_node_count(0)
    {
        if (x._M_root() == 0)
        {
            m_header->m_color = s_red;
            m_header->m_parent = 0;
            m_header->m_left = m_header;
            m_header->m_right = m_header;
        }
        else 
        {
            m_header->m_color = s_red;
            m_header->m_parent = m_copy(x.m_header->m_parent, m_header);
            m_header->m_left = minimum(m_header->m_parent);
            m_header->m_right = maximum(m_header->m_parent);
        }
        m_node_count = x.m_node_count;
    }
    Rb_tree(Rb_tree&& x)
    {
        m_header = x.m_header; 
        m_node_count = x.m_node_count;
        x.m_header = 0;
        x.m_node_count = 0;
    }
    ~Rb_tree()
    {
        clear(); 
        put_node(m_header);
    }
    Rb_tree&  operator=(const Rb_tree& x)
    {
        if (this != &x) 
        {
            clear();
            m_node_count = 0;
            if (x.m_header->m_parent == 0) 
            {
                m_header->m_parent = 0;
                m_header->m_left = m_header;
                m_header->m_right = m_header;
            }
            else 
            {
                m_header->m_parent = m_copy(x.m_header->m_parent, m_header);
                m_header->m_left = minimum(m_header->m_parent);
                m_header->m_right = maximum(m_header->m_parent);
                m_node_count = x.m_node_count;
            }
        }
        return *this;
    }
    Rb_tree&  operator=(Rb_tree&& x)
    {
        this->swap(x);
    }
public:
    iterator begin() { return m_header->m_left; }
    iterator end() { return m_header; }
    bool empty() const { return m_node_count == 0; }
    size_type size() const { return m_node_count; }
    void swap(Rb_tree& t)
    {
        std::swap(m_header, t.m_header);
        std::swap(m_node_count, t.m_node_count);
    }
    std::pair<iterator, bool> insert_unique(const value_type& v)
    {
        {
            link_type y = m_header;
            link_type x = m_header->m_parent;   // 从根节点开始
            bool comp = true;
            while (x != 0)            // 从根节点开始,往下寻找适当的插入点
            {
                y = x;
                comp = m_key_compare(m_keyOfValue(v), m_keyOfValue(x->m_value_field)); // v键值是否小于节点键值?
                x = comp ? x->m_left : x->m_right;                   // 遇到“大”则往左,遇到“小或等于”则往右
            }                                                   // y即为插入点的父节点
            iterator j = iterator(y);                           // 令迭代器j指向插入点的父节点y
            if (comp)                                           // 如果离开while循环是comp为真,将插入左侧
            {
                if (j == begin())                                // 如果插入点的父节点为最左节点
                {
                    return std::pair<iterator, bool>(m_insert(x, y, v), true); // x为插入点,y为插入点的父节点,v为新值
                }
                else
                {
                    --j;                                        // 减一用于下一步验证,即递减一次就小于新值,不能是等于
                }
            }

            if (m_key_compare(m_keyOfValue(j.m_node->m_value_field), m_keyOfValue(v))) // 确保此处的j小于v, 小于新值
            {
                return std::pair<iterator, bool>(m_insert(x, y, v), true);// x为插入点,y为插入点的父节点,v为新值
            }
            return std::pair<iterator, bool>(j, false); //此处,新值必与树中键值重复,返回错误
        }
    }
    iterator insert_equal(const value_type& v)
    {
        link_type y = m_header;
        link_type x = m_header->m_parent;
        while (x != 0)
        {
            y = x;
            x = m_key_compare(m_keyOfValue(v), m_keyOfValue(x->m_value_field)) ? x->m_left : x->m_right;
        }
        return m_insert(x, y, v);
    }
    // 删除节点x
    void erase(iterator x)
    {
        link_type y =
            (link_type)rb_tree_rebalance_for_erase(x.m_node,
                m_header->m_parent,
                m_header->m_left,
                m_header->m_right);
        destroy_node(y);
        --m_node_count;
    }
    size_type erase(const key_type& x)
    {
        std::pair<iterator, iterator> p = equal_range(x);
        size_type n = 0;
        iterator first = p.first;
        while ( first != p.second ) 
        {
            ++first;
            ++n;
        }
        erase(p.first, p.second);
        return n;
    }
    void erase(iterator first, iterator last)
    {
        if (first == begin() && last == end())
        {
            clear();
        }
        else
        {
            while (first != last)
            {
                erase(first++);
            }
        }
    }
    void clear() 
    {
        if (m_node_count != 0) 
        {
            m_erase(m_header->m_parent);
            m_header->m_left = m_header;
            m_header->m_parent = 0;
            m_header->m_right = m_header;
            m_node_count = 0;
        }
    }
    iterator find(const key_type& k)
    {
        link_type y = m_header;      
        link_type x = m_header->m_parent;      

        while (x != 0)
        {
            if (!m_key_compare(m_keyOfValue(x->m_value_field), k)) // 此处x向左移动
            {
                y = x, x = x->m_left;
            }
            else
            {
                x = x->m_right;              // 此处x向右移动
            }
        }
        iterator j = iterator(y);
        return (j == end() || m_key_compare(k, m_keyOfValue(j.m_node->m_value_field))) ? end() : j;
    }
    size_type count(const key_type& x) const
    {
        std::pair<iterator, iterator> p = equal_range(x);
        size_type n = 0;
        iterator first = p.first;
        while (first != p.second)
        {
            ++first;
            ++n;
        }
        return n;
    }
};

 

typename
posted @ 2021-02-27 16:02  ho966  阅读(106)  评论(0编辑  收藏  举报