c++ || STL关联式容器

本文详细介绍了C++ STL中的关联式容器,包括pair类模板、map映射表、multimap多重映射表和set集合。关联式容器通过键值对存储元素,自动排序并提供高效的查找、访问、插入和删除操作。map和multimap允许键值对的键不可重复,而set和multiset则存储值相等的键值对,不允许重复值。底层实现通常采用红黑树。
摘要由CSDN通过智能技术生成

关联式容器

  • 关联式容器与序列式容器不同的是,关联式容器在存储元素时还会为每个元素在配备一个键,整体以键值对的方式存储到容器中
  • 关联式容器可以通过键值对< key,value >直接找到对应元素,无序遍历整个容器
  • 管理师容器在存储元素,默认会根据各元素键值的大小做升序排序
  • 关联式容器查找、访问、插入和删除指定元素的效率更高
关联式容器名称特点
map定义在 < map> 头文件中,使用该容器存储的数据,其各个元素的键必须是唯一的(即不能重复),该容器会根据各元素键的大小,默认进行升序排序(调用 std::less< T>)。
set定义在 < set> 头文件中,使用该容器存储的数据,各个元素键和值完全相同,且各个元素的值不能重复(保证了各元素键的唯一性)。该容器会自动根据各个元素的键(其实也就是元素值)的大小进行升序排序(调用 std::less< T>)。
multimap定义在 < map> 头文件中,和 map 容器唯一的不同在于,multimap 容器中存储元素的键可以重复
multiset定义在 < set> 头文件中,和 set 容器唯一的不同在于,multiset 容器中存储元素的值可以重复(一旦值重复,则意味着键也是重复的)。

pair类模板

  • <“C语言教程”, “http://c.biancheng.net/c/”>
    第一个元素作为键(key),第二个元素作为值(value),由于**“键值对”并不是普通类型数据,因此STL标准库提供pair类模板**,专门用来将2个普通元素first和second(可以是c++基本数据类型,结构体,类自定义的类型)创建成一个新元素< first,second>.
  • pair类模板定义在< utility >头文件中
#1) 默认构造函数,即创建空的 pair 对象
pair();
#2) 直接使用 2 个元素初始化成 pair 对象
pair (const first_type& a, const second_type& b);
#3) 拷贝(复制)构造函数,即借助另一个 pair 对象,创建新的 pair 对象
template<class U, class V> pair (const pair<U,V>& pr);
#4) 移动构造函数
template<class U, class V> pair (pair<U,V>&& pr);
#5) 使用右值引用参数,创建 pair 对象
template<class U, class V> pair (U&& a, V&& b);
#include <iostream>
#include <utility>      // pair
#include <string>       // string
using namespace std;
int main() {
    // 调用构造函数 1,也就是默认构造函数
    pair <string, double> pair1;
    pair1.first = "数学教程";
    // 调用第 2 种构造函数
    pair <string, string> pair2("STL教程", "http://c.biancheng.net/stl/");
    // 调用拷贝构造函数
    pair <string, string> pair3(pair2);
    //调用移动构造函数
    pair <string, string> pair4(make_pair("C++教程", "http://c.biancheng.net/cplus/"));
    //调用了 make_pair() 函数,它也是 <utility> 头文件提供的,其功能是生成一个 pair 对象。
    // 因此,当我们将 make_pair()函数的返回值(是一个临时对象)作为参数传递给 pair()构造函数时,
    // 其调用的是移动构造函数,而不是拷贝构造函数。
    // 调用第 5 种构造函数
    pair <string, string> pair5(string("Python教程"), string("http://c.biancheng.net/python/"));

    cout << "pair1: " << pair1.first << " " << pair1.second << endl;
    cout << "pair2: " << pair2.first << " " << pair2.second << endl;
    cout << "pair3: " << pair3.first << " " << pair3.second << endl;
    cout << "pair4: " << pair4.first << " " << pair4.second << endl;
    cout << "pair5: " << pair5.first << " " << pair5.second << endl;
    //pair对象还可以进行比较 first相同则在比较second 都相同
    if (pair4 != pair2) {
        cout << "pair != pair2" << endl;
    }
    //pair2和pair3的key相同,value不同
    if (pair2 != pair3) {
        cout << "pair2 != pair3" << endl;
    }

    pair <string, int> pair8("pair", 10);
    pair <string, int> pair9("pair2", 20);
    //交换 pair8 和 pair9 的键值对
    pair8.swap(pair9);
    cout << "pair8: " << pair8.first << " " << pair8.second << endl;
    cout << "pair9: " << pair9.first << " " << pair9.second << endl;
    return 0;
}

map 映射表

  • map容器存储的都是pair对象,因此各个键值对的键和值可以是任意数据类型
    该容器存储的都是 pair<const K, T> 类型(其中 K 和 T 分别表示键和值的数据类型)的键值对元素。
  • 使用该容器存储的数据,其各个元素的键必须是唯一的且不能被修改(即不能重复),该容器会根据各元素键的大小,默认进行升序排序(调用 std::less< T>)
  • 手动指定map容器的排序规则,std::greater< T >
  • 底层是红黑树
成员方法功能
begin()返回指向容器中第一个(注意,是已排好序的第一个)键值对的双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
end()返回指向容器最后一个元素(注意,是已排好序的最后一个)所在位置后一个位置的双向迭代器,通常和 begin() 结合使用。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
rbegin()返回指向最后一个(注意,是已排好序的最后一个)元素的反向双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器。
rend()返回指向第一个(注意,是已排好序的第一个)元素所在位置前一个位置的反向双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器。
cbegin()和 begin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。
cend()和 end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。
crbegin()和 rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。
crend()和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。
find(key)map 容器中查找键为 key 的键值对,如果成功找到,则返回指向该键值对的双向迭代器;反之,则返回和 end() 方法一样的迭代器。另外,如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
lower_bound(key)返回一个指向当前 map 容器中第一个大于或等于 key 的键值对的双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
upper_bound(key)返回一个指向当前 map 容器中第一个大于 key 的键值对的迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
equal_range(key)该方法返回一个 pair 对象(包含 2 个双向迭代器),其中 pair.first 和 lower_bound() 方法的返回值等价,pair.second 和 upper_bound() 方法的返回值等价。也就是说,该方法将返回一个范围,该范围中包含的键为 key 的键值对(map 容器键值对唯一,因此该范围最多包含一个键值对)。
empty()若容器为空,则返回 true;否则 false。
size()返回当前 map 容器中存有键值对的个数。
max_size()返回 map 容器所能容纳键值对的最大个数,不同的操作系统,其返回值亦不相同。
operator[]map容器重载了 [] 运算符,只要知道 map 容器中某个键值对的键的值,就可以向获取数组中元素那样,通过键直接获取对应的值。
at(key)找到 map 容器中 key 键对应的值,如果找不到,该函数会引发 out_of_range 异常。
insert()map 容器中插入键值对
erase()删除 map 容器指定位置、指定键(key)值或者指定区域内的键值对。后续章节还会对该方法做重点讲解。
swap()交换 2 个 map 容器中存储的键值对,这意味着,操作的 2 个键值对的类型必须相同
clear()清空 map 容器中所有的键值对,即使 map 容器的 size() 为 0。
emplace()在当前 map 容器中的指定位置处构造新键值对。其效果和插入键值对一样,但效率更高
emplace_hint()在本质上和 emplace() 在 map 容器中构造新键值对的方式是一样的,不同之处在于,使用者必须为该方法提供一个指示键值对生成位置的迭代器,并作为该方法的第一个参数。
count(key)在当前 map 容器中,查找键为 key 的键值对的个数并返回。注意,由于 map 容器中各键值对的键的值是唯一的,因此该函数的返回值最大为 1。

map容器类模板

template < 
    class Key,                                     // 指定键(key)的类型
    class T,                                       // 指定值(value)的类型
    class Compare = less<Key>,                     // 指定排序规则
    class Alloc = allocator<pair<const Key, T> >   // 指定分配器对象的类型
> class map;
//后两个参数有默认值

创建map容器


int main()
{
    //创建空的map容器
    map<string, int> mymap; 
    
    //创建同时进行初始化
    map<string, int> mymap1{ {"sj",2} };
    map<string, int> mymap2{ make_pair("SJ",9) };

    //利用已创建好的容器在创建一个新的map容器
    map<string, int> mymap3(mymap1);
    //c++11标准中,增添了移动构造函数
    //当有临时的map对象作为参数,传递给要初始化的map容器时,会调用移动构造函数
    //创建一个会返回临时 map 对象的函数
    map<string, int> disMap(){
        map<string, int> tempMap{ {"C语言教程",10},{"STL教程",20} };
        return tempMap;
     }
    //调用 map 类模板的移动构造函数创建 newMap 容器
    map<string, int> newMap(disMap());

    //取已创建好的map容器指定区域内的键值对,初始化新的map容器
    map<string, int>myMap4{ {"C语言教程",10},{"STL教程",20} };
    map<string, int>newMap(++myMap4.begin(), myMap4.end());
    //键值对的键的大小按升序排列
    map<string, int, std::less<std::string> >myMap{ {"C语言教程",10},{"STL教程",20} };
    //键值对的键的大小按降序排列
    map<string, int, std::greater<std::string> >myMap{ {"C语言教程",10},{"STL教程",20} };
}

map的相应操作

在这里插入图片描述

int main()
{
    map<string, int> m{ {"SJ",9},{"EunHae",2},{"LHJ",1} ,{"LEETURK",1} };
    //会按照键的大小排序
 
    cout << "map容器的大小是: " << m.size() << endl;
    //从前到后遍历容器
    for (auto i = m.begin(); i != m.end(); i++)
    {        //第一个           最后一个的后一个
        cout << i->first << "  " << i->second << endl;
    }
    cout << endl;
    m.insert({ "LDH", 1 });
    m.emplace("YeSung",1 );   //更高效 
    for (auto i = m.rbegin(); i != m.rend(); i++)
    {        //最后一个          第一个的前一个
        cout << i->first << "  " << i->second << endl;
    }
    cout << endl;

    cout << "map容器是否为空: " << m.empty() << endl;
    cout << m.at("YeSung") << endl;  //输出键对应的值
    auto i = m.find("LDH");          //find找到键对应的键值对 返回相应的双向迭代器
    cout << i->first <<" " <<i->second << endl;
  
  //获取键对应的值
    int n = m["SJ"];
    cout << n << endl;
    return 0;
}

  • emplace和emplace_hint

emplace方法的返回值是一个pair对象,pair.first为一个迭代器,pair. second是一个bool类型变量

  1. 当该方法将键值对成功插入到 map 容器中时,其返回的迭代器指向该新插入的键值对,同时 bool 变量的值为 true;
  2. 插入失败时,则表明 map 容器中存在具有相同键的键值对,此时返回的迭代器指向此具有相同键的键值对,同时 bool 变量的值为 false。
int main()
{
    //创建并初始化 map 容器
    std::map<string, string>mymap;
    //插入键值对
    pair<map<string, string>::iterator, bool> ret = mymap.emplace("STL教程", "http://c.biancheng.net/stl/");
    cout << "1、ret.iter = <{" << ret.first->first << ", " << ret.first->second << "}, " << ret.second << ">" << endl;
    //插入新键值对
    ret = mymap.emplace("C语言教程", "http://c.biancheng.net/c/");
    cout << "2、ret.iter = <{" << ret.first->first << ", " << ret.first->second << "}, " << ret.second << ">" << endl;
    //失败插入的样例
    ret = mymap.emplace("STL教程", "http://c.biancheng.net/java/");
    cout << "3、ret.iter = <{" << ret.first->first << ", " << ret.first->second << "}, " << ret.second << ">" << endl;
    return 0;
}

emplace_hint()

  1. 该方法不仅要传入创建键值对所需要的数据,还需要传入一个迭代器作为第一个参数指明要插入的位置(新键值对键会插入到该迭代器指向的键值对的前面);
  2. 该方法的返回值是一个迭代器,而不再是 pair 对象。当成功插入新键值对时,返回的迭代器指向新插入的键值对;反之,如果插入失败,则表明 map 容器中存有相同键的键值对,返回的迭代器就指向这个键值对。
int main()
{
    //创建并初始化 map 容器
    std::map<string, string>mymap;
    //指定在 map 容器插入键值对
    map<string, string>::iterator iter = mymap.emplace_hint(mymap.begin(),"STL教程", "http://c.biancheng.net/stl/");
    cout << iter->first << " " << iter->second << endl;
    iter = mymap.emplace_hint(mymap.begin(), "C语言教程", "http://c.biancheng.net/c/");
    cout << iter->first << " " << iter->second << endl;
    //插入失败样例
    iter = mymap.emplace_hint(mymap.begin(), "STL教程", "http://c.biancheng.net/java/");
    cout << iter->first << " " << iter->second << endl;
    return 0;
}

multimap 多重映射表

  • multimap容器具有和map相同的特性,multimap容器用于存储pair< const K, T>类型的键值对,各个键值对的键的值不能做修改,并且该容器也会自行根据键的大小对存储的所有键值对进行排序操作
  • 数据按key有序 底层是红黑树
  • multimap容器中可以同时存储多个键相同的键值对

multimap容器包含的成员方法与map容器相比,没有提供at()成员方法,也没有重载 [ ] 运算符
–>不能按照键值来获取指定键值对,因为有可能指定的键对应着多个键值对,而不是1个


int main()       //multimap容器相关操作
{
    //创建并初始化 multimap 容器
    multimap<char, int>mymultimap{ {'a',10},{'b',20},{'b',15}, {'c',30} };
    //输出 mymultimap 容器存储键值对的数量
    cout << mymultimap.size() << endl;
    //输出 mymultimap 容器中存储键为 'b' 的键值对的数量
    cout << mymultimap.count('b') << endl;
    for (auto iter = mymultimap.begin(); iter != mymultimap.end(); ++iter) {
        cout << iter->first << " " << iter->second << endl;
    }
    return 0;
}

int main()      //multimap容器的创建
{
    //默认构造函数 创建空的multimap容器
    multimap<string, string> mymultimap; 
    //创建同时进行初始化
    multimap<string, int> mymultimap{ {"C语言",2}, {"Python教程",3} };
    //借助 pair 类模板的构造函数来生成各个pair类型的键值对
    multimap<string, string>mymultimap{
    pair<string,string>{"C语言教程", "http://c.biancheng.net/c/"},
    pair<string,string>{ "Python教程", "http://c.biancheng.net/python/"},
    pair<string,string>{ "STL教程", "http://c.biancheng.net/stl/"}
    };
    //调用 make_pair() 函数,生成键值对元素
    //创建并初始化 multimap 容器
    multimap<string, string>mymultimap{
        make_pair("C语言教程", "http://c.biancheng.net/c/"),
        make_pair("Python教程", "http://c.biancheng.net/python/"),
        make_pair("STL教程", "http://c.biancheng.net/stl/")
    };
    multimap<string, string>newmultimap(mymultimap);
    multimap<char, int, std::greater<char>>mymultimap{ {'a',1},{'b',2} };
}

set 集合

特性

  • 不再以键值对的方式存储数据,因为 set 容器专门用于存储键和值相等的键值对,因此该容器中真正存储的是各个键值对的值(value)
  • 数据按key有序(自动排序)底层是红黑树(增删改查速度非常快)
  • set容器存储的各个元素的值必须各不相同
  • set容器没有强制对存储元素的类型做const修饰,因此set容器中存储的元素的值是可以修改的,但c++标准为了防止用户修改对此做了限制,正常情况下,用户无法修改set容器中的值

{<‘a’, 1>, <‘b’, 2>, <‘c’, 3>}
{<‘a’, ‘a’>, <‘b’, ‘b’>, <‘c’, ‘c’>}
set容器只能存储第2对键值对,无法存储第一组键值对

  • 使用set容器存储键值对时,只需要提供各键值对中的value值即可。
template < 
class T,                        // 键 key 和值 value 的类型
class Compare = less<T>,        // 指定 set 容器内部的排序规则
class Alloc = allocator<T>      // 指定分配器对象的类型
> class set;
  • set容器常用成员方法
成员方法功能
begin()返回指向容器中第一个(注意,是已排好序的第一个)元素的双向迭代器。如果 set 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
end()返回指向容器最后一个元素(注意,是已排好序的最后一个)所在位置后一个位置的双向迭代器,通常和 begin() 结合使用。如果 set 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
rbegin()返回指向最后一个(注意,是已排好序的最后一个)元素的反向双向迭代器。如果 set 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器。
rend()返回指向第一个(注意,是已排好序的第一个)元素所在位置前一个位置的反向双向迭代器。如果 set 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器。
cbegin()和 begin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的元素值。
cend()和 end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的元素值。
crbegin()和 rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的元素值。
crend()和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的元素值。
find(val)set 容器中查找值为 val 的元素,如果成功找到,则返回指向该元素的双向迭代器;反之,则返回和 end() 方法一样的迭代器。另外,如果 set 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
lower_bound(val)返回一个指向当前 set 容器中第一个大于或等于 val 的元素的双向迭代器。如果 set 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
upper_bound(val)返回一个指向当前 set 容器中第一个大于 val 的元素的迭代器。如果 set 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
equal_range(val)该方法返回一个 pair 对象(包含 2 个双向迭代器),其中 pair.first 和 lower_bound() 方法的返回值等价,pair.second 和 upper_bound() 方法的返回值等价。也就是说,该方法将返回一个范围,该范围中包含的值为 val 的元素(set 容器中各个元素是唯一的,因此该范围最多包含一个元素)。
empty()若容器为空,则返回 true;否则 false。
size()返回当前 set 容器中存有元素的个数。
max_size()返回 set 容器所能容纳元素的最大个数,不同的操作系统,其返回值亦不相同。
insert()向 set 容器中插入元素。
erase()删除 set 容器中存储的元素。
swap()交换 2 个 set 容器中存储的所有元素。这意味着,操作的 2 个 set 容器的类型必须相同。
clear()清空 set 容器中所有的元素,即令 set 容器的 size() 为 0。
emplace()在当前 set 容器中的指定位置直接构造新元素。其效果和 insert() 一样,但效率更高。
emplace_hint()在本质上和 emplace() 在 set 容器中构造新元素的方式是一样的,不同之处在于,使用者必须为该方法提供一个指示新元素生成位置的迭代器,并作为该方法的第一个参数。
count(val)在当前 set 容器中,查找值为 val 的元素的个数,并返回。注意,由于 set 容器中各元素的值是唯一的,因此该函数的返回值最大为 1
  • 创建set容器的方法
set<string> retSet()
{
    std::set<std::string> myset{ "http://c.biancheng.net/java/",
                            "http://c.biancheng.net/stl/",
                            "http://c.biancheng.net/python/" };
    return myset;
}
int main()
{
    set<string> myset;  //set默认升序排序 空set容器
    set<string> myset1{ "SJ","YeSung"};    //创建时初始化
    set<string> copyset(myset1);   //拷贝构造函数

    std::set<std::string> copyset(retSet());   //移动构造函数 创建新容器的同时利用临时的set容器对其进行初始化

    std::set<std::string> myset{ "http://c.biancheng.net/java/",
                    "http://c.biancheng.net/stl/",
                    "http://c.biancheng.net/python/" };
    std::set<std::string> copyset(++myset.begin(), myset.end());//set容器中部分元素进行初始化

    std::set<std::string, std::greater<string> > myset{
    "http://c.biancheng.net/java/",
    "http://c.biancheng.net/stl/",
    "http://c.biancheng.net/python/" };  
     //修改排序方式为降序排序

} 
  • set相关操作
int main()
{
    set<string> myset{ "SJ","YeSung" ,"LeeTurk","LDH","LDH"};    //创建时初始化
    for (auto i = myset.begin(); i != myset.end(); i++)
    {
        cout << *i << " ";
    }
    cout << endl;
    for (auto i = myset.rbegin(); i != myset.rend(); i++)    //逆向
    {
        cout << *i << " ";
    }
    cout << endl;
    auto my = myset.find("YeSung");  //返回成功找到的双向迭代器
    cout << *my << endl;
    auto upper = myset.upper_bound("LDH");   //返回第一个大于val的双向迭代器
    cout << *upper << endl;
    auto lower = myset.lower_bound("LeeTurk");   //返回第一个大于或等于val的双向迭代器
    cout << *lower << endl;
    cout << myset.max_size() << endl;   //能容纳元素最大个数
    myset.insert("LHJ");                ///插入
    for (auto i = myset.begin(); i != myset.end(); i++)
    {
        cout << *i << " ";
    }
    cout << endl;
    cout << myset.count("LDH") << endl;     //val元素的个数 最大为1
        //向 myset 容器中添加元素
    pair<set<string, string>::iterator, bool> ret = myset.emplace("http://c.biancheng.net/stl/");
    cout << "myset size = " << myset.size() << endl;
    cout << "ret.iter = <" << *(ret.first) << ", " << ret.second << ">" << endl;
    return 0;
}
  • erase()和 clear()
//删除 set 容器中值为 val 的元素
//返回成功删除的元素个数
size_type erase (const value_type& val);
//后两种返回都是迭代器,指向set容器删除元素之后的第一个元素
//删除是最后一个元素 则返回最后一个元素的之后的位置,相当于end()方法返回的迭代器
//删除 position 迭代器指向的元素
iterator  erase (const_iterator position);
//删除 [first,last) 区间内的所有元素
iterator  erase (const_iterator first, const_iterator last);

void clear(); //删除容器中所有元素
int main()
{
    //创建并初始化 set 容器
    std::set<int>myset{ 1,2,3,4,5 };
    cout << "myset size = " << myset.size() << endl;

    //1) 调用第一种格式的 erase() 方法
    int num = myset.erase(2); //删除元素 2,myset={1,3,4,5}
    cout << "1、myset size = " << myset.size() << endl;
    cout << "num = " << num << endl;
    //2) 调用第二种格式的 erase() 方法
    set<int>::iterator iter = myset.erase(myset.begin()); //删除元素 1,myset={3,4,5}
    cout << "2、myset size = " << myset.size() << endl;
    cout << "iter->" << *iter << endl; //3
    //3) 调用第三种格式的 erase() 方法
    set<int>::iterator iter2 = myset.erase(myset.begin(), --myset.end());//删除元素 3,4,myset={5}
    cout << "3、myset size = " << myset.size() << endl;
    cout << "iter2->" << *iter2 << endl;//5


    myset.clear(); //删除所有元素
    return 0;
}

multiset 多重集合

特性

  • 不再以键值对的方式存储数据,multiset 容器专门用于存储键和值相等的键值对,因此该容器中真正存储的是各个键值对的值(value)
  • multiset 容器在存储数据时,会根据各元素值的大小对存储的元素进行排序(默认做升序排序);
  • 存储到 multiset 容器中的元素,虽然其类型没有明确用 const 修饰,但正常情况下它们的值是无法被修改的;
  • multiset容器可以存储多个值相同的元素
包含的方法和使用的规则如同set容器,不再赘述
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值