map,set,multimap,multiset的异同

map的特性是,所有元素都会依据元素的键值自动排序,map的所有元素都是键值对,同时有实值value和键值key。键值对的第一元素被视为键值,第二元素视为实值,map不允许两个元素有相同的实值。

注意:不可以通过map的迭代器改变map的元素值,因为set的元素值就是键值,关系到set的排列规则,如果任意改变set的元素值,会严重弄破坏set组织。如果想要修正元素的实值,答案是可以,因为map的元素的实值不影响map元素的排列规则,map迭代器既不是一种constant iterators,也不是一种mutable iterators
//map的底层实现--借助红黑树

#include <map>
#include<string>
#include<iostream>
using namespace std;



enum COLOR{RED, BLACK};

template<class K, class V>
struct RBTreeNode
{
    RBTreeNode(const K& key = K(), const V& value = V(), const COLOR& color = RED)
        : _pLeft(NULL)
        , _pRight(NULL)
        , _pParent(NULL)
        , _key(key)
        , _value(value)
        , _color(color)
    {}

    RBTreeNode<K, V>* _pLeft;
    RBTreeNode<K, V>* _pRight;
    RBTreeNode<K, V>* _pParent;
    K _key;
    V _value;
    COLOR _color;
};

template<class K, class V, class Ref, class Ptr>
class RBTreeIterator
{
    typedef RBTreeNode<K, V> Node;
    typedef RBTreeIterator<K, V, Ref, Ptr> Iterator;
public:
    RBTreeIterator()
        :_pNode(NULL)
    {}

    RBTreeIterator(Node* pNode)
        :_pNode(pNode)
    {}

    RBTreeIterator(const Iterator& s)
        :_pNode(s._pNode)
    {}

    Iterator& operator++()
    {
        Increment();
        return *this;
    }

    Iterator operator++(int)
    {
        Iterator temp(*this);
        Increment(_pNode);
        return temp;
    } 

    Iterator& operator--()
    {
        Decrement(_pNode);
        return *this;
    }

    Iterator operator--(int)
    {
        Iterator temp(*this);
        Decrement(_pNode);
        return temp;
    }

    Ref operator*()
    {
        return _pNode->_key;
    }

    Ptr operator->()
    {
        return &(operator*());
    }

    bool operator!=(const Iterator& s)
    {
        return _pNode != s._pNode;
    }

    bool operator==(const Iterator& s)
    {
        return !(*this != s);
    }

protected:
    Node* Increment()
    {
        Node* pNode = _pNode;
        // 如果当前节点的右子树存在,找右子树中最左边的结点
        if(pNode->_pRight)
        {
            pNode = pNode->_pRight;
            while(pNode->_pLeft)
                pNode = pNode->_pLeft;
        }
        else
        {
            Node* parent = pNode->_pParent;
            while(parent->_pRight == pNode)
            {
                pNode = parent;
                parent = pNode->_pParent;
            }
            // 特殊--> 根节点没有右子树,获取根节点的下一个结点
            //if(parent->parent != pNode)
            if(pNode->_pRight != parent)
                pNode = parent;
        }
        _pNode = pNode;
        return pNode;
    }

    Node* Decrement(Node* pNode)
    {
        if(pNode->_color == RED && pNode->_pParent->_pParent == pNode)
        {
            pNode = pNode->_pRight;
        }
        // 如果当前节点的左子树存在,找左子树中最右边的结点
        else if(pNode->_pLeft)
        {
            pNode = pNode->_pLeft;
            while(pNode->_pRight)
                pNode = pNode->_pRight;
        }
        else
        {
            Node* parent = pNode->_pParent;
            while(pNode->_pLeft == pNode)
            {
                pNode = parent;
                parent = pNode->_pParent;
            }
            pNode = parent;
        }
        _pNode = pNode;
        return pNode;
    }
private:
    Node* _pNode;
};

template<class K, class V>
class RBTree
{
    typedef RBTreeNode<K, V> Node;
public:
    typedef RBTreeIterator<K, V, K&, K*> Iterator;
public:
    RBTree()
        :_size(0)
    {
        _pHead = new Node();
        _pHead->_pLeft = _pHead;
        _pHead->_pRight = _pHead;
        _pHead->_pParent = NULL;
        _pHead->_color = RED;
    }

    Iterator Begin()
    {
        return Iterator(_pHead->_pLeft);
    }

    Iterator End()
    {
        return Iterator(_pHead);
    }

    bool Empty()
    {
        return _pHead->_pParent == NULL;
    }

    size_t Size()
    {
        return _size;
    }

    Iterator Find(const K& key)
    {
        Node* pCur = _pHead->_pParent;
        while(pCur)
        {
            if(pCur->_key == key)
                return Iterator(pCur);
            else if(key < pCur->_key)
                pCur = pCur->_pLeft;
            else
                pCur = pCur->_pRight;
        }
        return Iterator(NULL);
    }

    bool Insert(const K& key, const V& value)
    {
        Node* _pRoot = _pHead->_pParent;
        //判断树是否存在
        if(_pRoot == NULL)
        {
            _pRoot = new Node(key, value, BLACK);
            _pHead->_pParent = _pRoot;
            _pRoot->_pParent = _pHead;

            return true;
        }
        else
        {
            //找插入位置
            Node* pCur = _pRoot;
            Node* parent = NULL;
            while(pCur)
            {
                if(key > pCur->_key)
                {
                    parent = pCur;
                    pCur = pCur->_pRight;
                }

                else if(key < pCur->_key)
                {
                    parent = pCur;
                    pCur = pCur->_pLeft;
                }

                else
                    return false;
            }
            //插入节点
            pCur = new Node(key, value);
            if(key > parent->_key)
                parent->_pRight = pCur;
            else
                parent->_pLeft = pCur;

            pCur->_pParent = parent;

            Node* pGrangFather = parent->_pParent;
            有可能违反红黑树性质
            while(pCur != _pRoot && parent->_color == RED)
            {
                //双亲的双亲一定存在,且为黑色
                if(parent == pGrangFather->_pLeft)
                {
                    Node* pUncle = pGrangFather->_pRight;
                    //情况一:pCur为红色,parent为红色,grand为黑色,uncle为红色
                    if(pUncle && pUncle->_color == RED )
                        //uncle存在且为红
                    {
                        parent->_color = BLACK;
                        pUncle->_color = BLACK;
                        pGrangFather->_color = RED;

                        pCur = pGrangFather;
                        parent = pGrangFather->_pParent;
                    }
                    //uncle不存在或者uncle存在且为黑色
                    else
                    {
                        //情况三:pCur为parent的右孩子
                        if(parent->_pRight == pCur)
                        {
                            _RotateL(parent);
                            swap(pCur, parent);
                        }

                        //情况二:pCur为parent的左孩子
                        parent->_color = BLACK;
                        pGrangFather->_color = RED;
                        _RotateR(pGrangFather);
                    }
                }
                else
                {
                    Node* pUncle = pGrangFather->_pLeft;
                    //情况一:uncle存在而且uncle为红色
                    if(pUncle && pUncle->_color == RED)
                    {
                        parent->_color = BLACK;
                        pUncle->_color = BLACK;
                        pGrangFather->_color = RED;

                        pCur = pGrangFather;
                        parent = pGrangFather->_pParent;
                    }
                    //uncle不存在或者uncle存在且为黑色
                    else
                    {
                        //情况三:pCur为parent的左孩子
                        if(pCur == parent->_pLeft)
                        {
                            _RotateR(parent);
                            swap(parent, pCur);
                        }

                        //情况二:pCur为parent的左孩子
                        parent->_color = BLACK;
                        pGrangFather->_color = RED;
                        _RotateL(pGrangFather);
                    }
                }
            }
            _pRoot->_color = BLACK;
            _pHead->_pParent = _pRoot;
            _pRoot->_pParent = _pHead;

        }
        _size++;
        _pHead->_pLeft = GetMinNode();
        _pHead->_pRight = GetMaxNode();
        return true;
    }

    void InOrder()
    {
        cout<<" InOrder: ";
        _InOrder(_pHead->_pParent);
        cout<<endl;
    }

    bool CheckRBTree()
    {
        //判断是否满足红黑树
        if(_pHead->_pParent == NULL)
            return true;

        if(_pHead->_pParent->_color == RED)
        {
            cout<<"根结点是红色的,不满足性质二"<<endl;
            return false;
        }

        size_t count = 0;
        Node* pCur = _pHead->_pParent;
        while (pCur)
        {
            if (pCur->_color == BLACK)
                count++;
            pCur = pCur->_pLeft;
        }//统计最左边路径上得黑色结点数

        size_t num = 0;

        return CheckColour(_pHead->_pParent) && _CheckRBTree(_pHead->_pParent, count, num);
    }


    bool CheckColour(Node* pRoot)
    {//检查颜色
        if (pRoot)
            return true;

        Node* parent = pRoot->_pParent;
        if (pRoot->_color == RED && parent->_color == RED)
        {
            cout<<"有两个连续的红色节点数,不满足性质3"<<endl;
            return false;
        }

        return CheckColour(pRoot->_pLeft) && CheckColour(pRoot->_pRight);
    }

protected:


    Node* GetMaxNode()
    {
        Node* pCur = _pHead->_pParent;
        while(pCur->_pRight)
            pCur = pCur->_pRight;

        return pCur;
    }

    Node* GetMinNode()
    {
        Node* pCur = _pHead->_pParent;
        while(pCur->_pLeft)
            pCur = pCur->_pLeft;

        return pCur;
    }

    void _RotateL(Node* parent)
    {
        Node* pSubR = parent->_pRight;
        Node* pSubRL = pSubR->_pLeft;

        parent->_pRight = pSubRL;
        if(pSubRL)
            pSubRL->_pParent = parent;

        pSubR->_pLeft = parent;
        Node* pparent = parent->_pParent;
        parent->_pParent = pSubR;
        pSubR->_pParent = pparent;

        if(pparent == NULL)
            _pHead->_pParent = pSubR;
        else
        {
            if(parent == pparent->_pLeft)
                pparent->_pLeft = pSubR;
            else
                pparent->_pRight = pSubR;
        }
    }

    void _RotateR(Node* parent)
    {
        Node* pSubL = parent->_pLeft;
        Node* pSubLR = pSubL->_pRight;

        parent->_pLeft = pSubLR;
        if(pSubLR)
            pSubLR->_pParent = parent;
        pSubL->_pRight = parent;
        Node* pparent = parent->_pParent;
        parent->_pParent = pSubL;
        pSubL->_pParent = pparent;
        if(pparent == NULL)
            _pHead->_pParent = pSubL;
        else
        {
            if(parent == pparent->_pLeft)
                pparent->_pLeft = pSubL;
            else
                pparent->_pRight = pSubL;
        }
    }

    void _InOrder(Node* pRoot)
    {
        if(pRoot)
        {
            _InOrder(pRoot->_pLeft);
            cout<<pRoot->_key<<" ";
            _InOrder(pRoot->_pRight);
        }
    }

    bool _CheckRBTree(Node* pRoot, const size_t blackCount, size_t k)
    {
        //检查黑色结点数目
        if (pRoot == NULL)
            return true;

        if (pRoot->_color == BLACK)
            ++k;

        if (pRoot->_pLeft == NULL && pRoot->_pRight == NULL && blackCount != k)
        {
            cout<<"每条路径上的黑色节点数不相等,不满足性质4"<<endl;
            return false;
        }

        return _CheckRBTree(pRoot->_pLeft, blackCount, k) && _CheckRBTree(pRoot->_pRight, blackCount, k);
    }

protected:
    Node* _pHead;
    size_t _size;
};


template<class K, class V>
class Map
{
public:
    typename typedef RBTreeIterator<K, V, K&, K*> Iterator;
public:
    Iterator Begin()
    {
        return t.Begin();
    }

    Iterator End()
    {
        return t.End();
    }

    bool Empty()const
    {
        return t.Empty();
    }

    bool Insert(const K& key, const V& value)
    {
        return t.Insert(key, value);
    }

    size_t Size()const
    {
        return t.Size();
    }

    Iterator Find(const K& key)
    {
        return t.Find(key);
    }


    bool operator != (const Iterator& s)
    {
        return _pNode != s._pNode;
    }

    bool operator == (const Iterator& s)
    {
        return !(*this != s);
    }

    V& operator[](const K& key)
    {
        Iterator it = t.Find(key);
        return (it._pNode)->_value;
    }

private:
    RBTree<K, V> t;
};

set的特性是,所有元素都会依据元素的键值自动排序,set 的所有元素就是实值,实值就是键值。set不允许两个元素有相同的实值。

注意:不可以通过set的迭代器改变set的元素值,因为set的元素值就是键值,关系到set的排列规则,如果任意改变set的元素值,会严重弄破坏set组织。set迭代器是一种constant iterators.


#include <set>
#include<string>
#include<iostream>
using namespace std;



enum COLOR{RED, BLACK};

template<class K, class V>
struct RBTreeNode
{
    RBTreeNode(const K& key = K(), const V& value = V(), const COLOR& color = RED)
        : _pLeft(NULL)
        , _pRight(NULL)
        , _pParent(NULL)
        , _key(key)
        , _value(value)
        , _color(color)
    {}

    RBTreeNode<K, V>* _pLeft;
    RBTreeNode<K, V>* _pRight;
    RBTreeNode<K, V>* _pParent;
    K _key;
    V _value;
    COLOR _color;
};

template<class K, class V, class Ref, class Ptr>
class RBTreeIterator
{
    typedef RBTreeNode<K, V> Node;
    typedef RBTreeIterator<K, V, Ref, Ptr> Iterator;
public:
    RBTreeIterator()
        :_pNode(NULL)
    {}

    RBTreeIterator(Node* pNode)
        :_pNode(pNode)
    {}

    RBTreeIterator(const Iterator& s)
        :_pNode(s._pNode)
    {}

    Iterator& operator++()
    {
        Increment();
        return *this;
    }

    Iterator operator++(int)
    {
        Iterator temp(*this);
        Increment(_pNode);
        return temp;
    } 

    Iterator& operator--()
    {
        Decrement(_pNode);
        return *this;
    }

    Iterator operator--(int)
    {
        Iterator temp(*this);
        Decrement(_pNode);
        return temp;
    }

    Ref operator*()
    {
        return _pNode->_key;
    }

    Ptr operator->()
    {
        return &(operator*());
    }

    bool operator!=(const Iterator& s)
    {
        return _pNode != s._pNode;
    }

    bool operator==(const Iterator& s)
    {
        return !(*this != s);
    }

protected:
    Node* Increment()
    {
        Node* pNode = _pNode;
        // 如果当前节点的右子树存在,找右子树中最左边的结点
        if(pNode->_pRight)
        {
            pNode = pNode->_pRight;
            while(pNode->_pLeft)
                pNode = pNode->_pLeft;
        }
        else
        {
            Node* parent = pNode->_pParent;
            while(parent->_pRight == pNode)
            {
                pNode = parent;
                parent = pNode->_pParent;
            }
            // 特殊--> 根节点没有右子树,获取根节点的下一个结点
            //if(parent->parent != pNode)
            if(pNode->_pRight != parent)
                pNode = parent;
        }
        _pNode = pNode;
        return pNode;
    }

    Node* Decrement(Node* pNode)
    {
        if(pNode->_color == RED && pNode->_pParent->_pParent == pNode)
        {
            pNode = pNode->_pRight;
        }
        // 如果当前节点的左子树存在,找左子树中最右边的结点
        else if(pNode->_pLeft)
        {
            pNode = pNode->_pLeft;
            while(pNode->_pRight)
                pNode = pNode->_pRight;
        }
        else
        {
            Node* parent = pNode->_pParent;
            while(pNode->_pLeft == pNode)
            {
                pNode = parent;
                parent = pNode->_pParent;
            }
            pNode = parent;
        }
        _pNode = pNode;
        return pNode;
    }
private:
    Node* _pNode;
};

template<class K, class V>
class RBTree
{
    typedef RBTreeNode<K, V> Node;
public:
    typedef RBTreeIterator<K, V, K&, K*> Iterator;
public:
    RBTree()
        :_size(0)
    {
        _pHead = new Node();
        _pHead->_pLeft = _pHead;
        _pHead->_pRight = _pHead;
        _pHead->_pParent = NULL;
        _pHead->_color = RED;
    }

    Iterator Begin()
    {
        return Iterator(_pHead->_pLeft);
    }

    Iterator End()
    {
        return Iterator(_pHead);
    }

    bool Empty()
    {
        return _pHead->_pParent == NULL;
    }

    size_t Size()
    {
        return _size;
    }

    Iterator Find(const K& key)
    {
        Node* pCur = _pHead->_pParent;
        while(pCur)
        {
            if(pCur->_key == key)
                return Iterator(pCur);
            else if(key < pCur->_key)
                pCur = pCur->_pLeft;
            else
                pCur = pCur->_pRight;
        }
        return Iterator(NULL);
    }

    bool Insert(const K& key, const V& value = V())
    {
        Node* _pRoot = _pHead->_pParent;
        //判断树是否存在
        if(_pRoot == NULL)
        {
            _pRoot = new Node(key, value, BLACK);
            _pHead->_pParent = _pRoot;
            _pRoot->_pParent = _pHead;

            return true;
        }
        else
        {
            //找插入位置
            Node* pCur = _pRoot;
            Node* parent = NULL;
            while(pCur)
            {
                if(key > pCur->_key)
                {
                    parent = pCur;
                    pCur = pCur->_pRight;
                }

                else if(key < pCur->_key)
                {
                    parent = pCur;
                    pCur = pCur->_pLeft;
                }

                else
                    return false;
            }
            //插入节点
            pCur = new Node(key, value);
            if(key > parent->_key)
                parent->_pRight = pCur;
            else
                parent->_pLeft = pCur;

            pCur->_pParent = parent;

            Node* pGrangFather = parent->_pParent;
            有可能违反红黑树性质
            while(pCur != _pRoot && parent->_color == RED)
            {
                //双亲的双亲一定存在,且为黑色
                if(parent == pGrangFather->_pLeft)
                {
                    Node* pUncle = pGrangFather->_pRight;
                    //情况一:pCur为红色,parent为红色,grand为黑色,uncle为红色
                    if(pUncle && pUncle->_color == RED )
                        //uncle存在且为红
                    {
                        parent->_color = BLACK;
                        pUncle->_color = BLACK;
                        pGrangFather->_color = RED;

                        pCur = pGrangFather;
                        parent = pGrangFather->_pParent;
                    }
                    //uncle不存在或者uncle存在且为黑色
                    else
                    {
                        //情况三:pCur为parent的右孩子
                        if(parent->_pRight == pCur)
                        {
                            _RotateL(parent);
                            swap(pCur, parent);
                        }

                        //情况二:pCur为parent的左孩子
                        parent->_color = BLACK;
                        pGrangFather->_color = RED;
                        _RotateR(pGrangFather);
                    }
                }
                else
                {
                    Node* pUncle = pGrangFather->_pLeft;
                    //情况一:uncle存在而且uncle为红色
                    if(pUncle && pUncle->_color == RED)
                    {
                        parent->_color = BLACK;
                        pUncle->_color = BLACK;
                        pGrangFather->_color = RED;

                        pCur = pGrangFather;
                        parent = pGrangFather->_pParent;
                    }
                    //uncle不存在或者uncle存在且为黑色
                    else
                    {
                        //情况三:pCur为parent的左孩子
                        if(pCur == parent->_pLeft)
                        {
                            _RotateR(parent);
                            swap(parent, pCur);
                        }

                        //情况二:pCur为parent的左孩子
                        parent->_color = BLACK;
                        pGrangFather->_color = RED;
                        _RotateL(pGrangFather);
                    }
                }
            }
            _pRoot->_color = BLACK;
            _pHead->_pParent = _pRoot;
            _pRoot->_pParent = _pHead;

        }
        _size++;
        _pHead->_pLeft = GetMinNode();
        _pHead->_pRight = GetMaxNode();
        return true;
    }

    void InOrder()
    {
        cout<<" InOrder: ";
        _InOrder(_pHead->_pParent);
        cout<<endl;
    }

    bool CheckRBTree()
    {
        //判断是否满足红黑树
        if(_pHead->_pParent == NULL)
            return true;

        if(_pHead->_pParent->_color == RED)
        {
            cout<<"根结点是红色的,不满足性质二"<<endl;
            return false;
        }

        size_t count = 0;
        Node* pCur = _pHead->_pParent;
        while (pCur)
        {
            if (pCur->_color == BLACK)
                count++;
            pCur = pCur->_pLeft;
        }//统计最左边路径上得黑色结点数

        size_t num = 0;

        return CheckColour(_pHead->_pParent) && _CheckRBTree(_pHead->_pParent, count, num);
    }


    bool CheckColour(Node* pRoot)
    {//检查颜色
        if (pRoot)
            return true;

        Node* parent = pRoot->_pParent;
        if (pRoot->_color == RED && parent->_color == RED)
        {
            cout<<"有两个连续的红色节点数,不满足性质3"<<endl;
            return false;
        }

        return CheckColour(pRoot->_pLeft) && CheckColour(pRoot->_pRight);
    }

protected:


    Node* GetMaxNode()
    {
        Node* pCur = _pHead->_pParent;
        while(pCur->_pRight)
            pCur = pCur->_pRight;

        return pCur;
    }

    Node* GetMinNode()
    {
        Node* pCur = _pHead->_pParent;
        while(pCur->_pLeft)
            pCur = pCur->_pLeft;

        return pCur;
    }

    void _RotateL(Node* parent)
    {
        Node* pSubR = parent->_pRight;
        Node* pSubRL = pSubR->_pLeft;

        parent->_pRight = pSubRL;
        if(pSubRL)
            pSubRL->_pParent = parent;

        pSubR->_pLeft = parent;
        Node* pparent = parent->_pParent;
        parent->_pParent = pSubR;
        pSubR->_pParent = pparent;

        if(pparent == NULL)
            _pHead->_pParent = pSubR;
        else
        {
            if(parent == pparent->_pLeft)
                pparent->_pLeft = pSubR;
            else
                pparent->_pRight = pSubR;
        }
    }

    void _RotateR(Node* parent)
    {
        Node* pSubL = parent->_pLeft;
        Node* pSubLR = pSubL->_pRight;

        parent->_pLeft = pSubLR;
        if(pSubLR)
            pSubLR->_pParent = parent;
        pSubL->_pRight = parent;
        Node* pparent = parent->_pParent;
        parent->_pParent = pSubL;
        pSubL->_pParent = pparent;
        if(pparent == NULL)
            _pHead->_pParent = pSubL;
        else
        {
            if(parent == pparent->_pLeft)
                pparent->_pLeft = pSubL;
            else
                pparent->_pRight = pSubL;
        }
    }

    void _InOrder(Node* pRoot)
    {
        if(pRoot)
        {
            _InOrder(pRoot->_pLeft);
            cout<<pRoot->_key<<" ";
            _InOrder(pRoot->_pRight);
        }
    }

    bool _CheckRBTree(Node* pRoot, const size_t blackCount, size_t k)
    {
        //检查黑色结点数目
        if (pRoot == NULL)
            return true;

        if (pRoot->_color == BLACK)
            ++k;

        if (pRoot->_pLeft == NULL && pRoot->_pRight == NULL && blackCount != k)
        {
            cout<<"每条路径上的黑色节点数不相等,不满足性质4"<<endl;
            return false;
        }

        return _CheckRBTree(pRoot->_pLeft, blackCount, k) && _CheckRBTree(pRoot->_pRight, blackCount, k);
    }

protected:
    Node* _pHead;
    size_t _size;
};


template<class K>
class Set
{
public:
    typename typedef RBTreeIterator<K, K, K&, K*> Iterator;
public:
    Iterator Begin()
    {
        return t.Begin();
    }

    Iterator End()
    {
        return t.End();
    }

    bool Empty()const
    {
        return t.Empty();
    }

    bool Insert(const K& key)
    {
        return t.Insert(key);
    }

    size_t Size()const
    {
        return t.Size();
    }

    Iterator Find(const K& key)
    {
        return t.Find(key);
    }


    bool operator != (const Iterator& s)
    {
        return _pNode != s._pNode;
    }

    bool operator == (const Iterator& s)
    {
        return !(*this != s);
    }

    K& operator[](const K& key)
    {
        Iterator it = t.Find(key);
        return (it._pNode)->_key;
    }

private:
    RBTree<K, K> t;
};

multiset的特性以及用法和set完全相同,唯一的差别在于它允许键值重复,因此他的操作采用的是底层机制RB-tree的insert-equal()

multimap的特性以及用法和map完全相同,唯一的差别在于它允许键值重复,因此他的操作采用的是底层机制RB-tree的insert-equal()

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值