序列式容器

序列式容器

概念

序列容器,就是以线性排列(类似普通数组的存储方式)来存储某一指定类型的数据,并且该类容器并不会自动对存储的元素按照值的大小进行排序。

内容

array(数组容器)表示可以存储N个T类型的容器,该容器一旦建立那么容量就是固定的,因此不能额外增加或删除元素,只能修改其值
vector(向量容器)大部分与array类似,但是vector是长度可变的,可以看成是可以动态变化长度的数组,因此在其尾部增加或删除元素效率可以看成是O(1),对其他位置的操作为O(n)
deque(双端队列容器)大部分与vector类似,但是deque在头部插入与删除也是O(1),在其中部插入与删除元素才是O(n)
list(链表容器)一个长度可变的T类型的序列,内部采用双向链表实现,由于双向链表的特性,对其在任何位置插入与删除都将是O(1),但是由于list对应的迭代器类型为双向迭代器,决定了list的访问必须逐个访问,因此对list的访问速度要慢于array、vector和deque
forward_list(正向链表容器)十分接近list,但是内部采用的是单链表实现,因此节省了一定的内存,但是访问也需要逐个访问

序列式容器迭代器的通用函数

由于序列式容器(除了forward_list外)都是支持随机访问迭代器的,所以他们有一些通用的对迭代器的操作方法,这里直接给出:

begin()返回指向第一个元素的随机访问迭代器
end()返回指向容器最后一个元素之后一个位置的随机访问迭代器
rbegin()返回指向最后一个元素的随机访问迭代器
rend()返回指向第一个元素之前一个位置的随机访问迭代器
cbegin()和 begin() 功能相同,只不过在其基础上增加了 const 属性,不能用于修改元素
cend()和 end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素
crbegin()和 rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素
crend()和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素

举个栗子:

std:: array<int, 5> v{1,2,3,4,5};
for (std::array<int, 5>::iterator it = v.begin(); it != v.end(); it++) {
    std::cout << *it << ' ';
}
std::cout << std::endl;//Output 1 2 3 4 5
for (std::array<int, 5>::reverse_iterator it = v.rbegin(); it != v.rend(); it++) {//注意此处需要使用反向迭代器和++
    std::cout << *it << ' ';
}
std::cout << std::endl;//Output 5 4 3 2 1

关于这几个对迭代器的操作方法,可以见下图:

除此之外对于迭代器的操作,可以使用auto关键字,编译器会自动识别其迭代器的类型。

序列式容器用法

array

array是一种不可以修改其长度,在普通数组的基础上增加了一些全局变量和函数的序列式容器,比起不同数组效率更高更安全。

定义

array的定义:

namespace std{
    template <typename T, size_t N>
    class array;
}

可以看到array被定义在命名空间std中

因此创建array可以有以下两种方式:

#include<array> //使用array必须导入array头文件
std::array<int, 5> v;

或:

#include<array>
using namespace std;
int main() {
	array<int, 5> v;
}
初始化

array的初始化可以采用默认初始化方式:

std:: array<int, 5> v{};//默认初始化
for (std::array<int, 5>::iterator it = v.begin(); it != v.end(); it++) {
	std::cout << *it << ' ';
}
//输出5个0

也可以采用类似数组的初始化方式:

std:: array<int, 10> v{1,2,5};
for (std::array<int, 10>::iterator it = v.begin(); it != v.end(); it++) {
    std::cout << *it << ' ';
}
//输出1 2 5 0 0 0 0 0 0 0
成员函数

array因为支持随机访问迭代器,所以上文提到的8个操作array都支持

除此之外,array还支持其他几个操作

size()返回array中的元素个数,由于array长度是固定的,所以相当于返回构造array的第二个参数N
max_size()返回可容纳元素的最大数量,由于array长度是固定的,所以相当于返回构造array的第二个参数N
empty()返回array是否非空
at(i)返回array第i个位置(从0开始)的元素的引用,这意味着我们可以直接对他进行修改,并且它会自动检查i是否会导致越界,如果越界会抛出std::out_of_range的异常
front()返回容器中第一个元素的直接引用,该函数不适用于空的array容器
back()返回容器中最后一个元素的直接应用,该函数同样不适用于空的array容器
data()返回一个指向容器首个元素的指针
operator[]类似于数组元素的下标访问方式,返回数组对应下标的引用,可以直接操作(包括修改)
fill(T& val)将val赋值给array中的每一个元素,相当于批量赋值,可以用于初始化
array1.swap(array2)在array1和array2的T与N一致的情况下交换两个array中的元素

举个栗子:

std:: array<int, 5> v{1,2,3,4,5};
v[2] = 7;
std::cout << v.at(2) << std::endl;//Output 7
v.at(2) = 5;
std::cout << v.at(2) << std::endl;//Output 5
for (std::array<int, 5>::iterator it = v.begin(); it != v.end(); it++) {//Output 1 2 5 4 5
	std::cout << *it << ' ';
}
std::cout << std::endl;
std::cout << v.front() << std::endl;//Output 1
std::cout << v.back() << std::endl;//Output 5
v.fill(10);
for (std::array<int, 5>::iterator it = v.begin(); it != v.end(); it++) {//Output 10 10 10 10 10
	std::cout << *it << ' ';
}
std::cout << std::endl;
*(v.data()) = 4;
for (std::array<int, 5>::iterator it = v.begin(); it != v.end(); it++) {//Output 4 10 10 10 10
	std::cout << *it << ' ';
}
array相比数组的提升

1.array可以实现大小的比较

大小比较的规则与字符串的大小比较规则一致

std::array<char, 20> h{ "helloworld" };
std::array<char, 20> a{ "apple" };	
if (a < h) {//no
    std::cout << "yes\n";
}
else {
    std::cout << "no\n";
}

2.array可以直接赋值

std::array<char, 20> h{ "helloworld" };
std::array<char, 20> a{ "apple" };
a = h;
for (auto e : a) {//helloworld
    std::cout << e;
}

3.at方法可以自动检查边界问题

vector

vector是向量容器,一个向量用于存储同一个类型的一组值

定义

一个向量的定义需要引入如下代码段:

#include <vector>
using namespace std;

或以下列形式创建,(T)是一个类型

#include <vector>
std::vector<T> v;

注意,这样定义的 vector 容器,因为容器中没有元素,所以没有为其分配空间。当添加第一个元素(比如使用 push_back() 函数)时,vector 会自动分配内存。

对于vector,有容量(capacity)和size(大小)的区分。

容量指的是vector能够容纳的最大元素数,这是一个可以拓展的值,通过调用reserve()来拓展容量,如果新的容量小于等于当前容量,那么没有操作,其不会影响原有元素,也不会生成新元素,但是要注意,一旦使用了reserve()那么原油的所有迭代器都将会失效,因为拓展容量时会修改整个vector的地址。

大小指的是vector当前实际容纳的元素数,其也可以修改,通过调用resize()来修改值,如果新的size比容量要大,那么会自动扩容。

初始化
第一种方式,初始化成员列表:
vector<int> v1{1,2,3,4,5};
第二种方式,指定元素个数(默认初始化为0)
vector<int> v2(5);//5个0
第三种方式,指定元素个数并全部初始化为1
vector<int> v3(5,1);//5个1 
第四种方式,存储已经存在的vector容器值或数组
vector<int> a(v3); //5个1
vector<int> b(v1.begin(),v1.begin()+3);//1 2 3
int arr[3] = {4,5,6};
vector<int> c(arr,arr+3);//4 5 6

需要注意的是,由于vector是可以直接创建空的vector的,所以这样的初始化是错误的

vector<int> v;
for (auto it = v.begin();it != v.end();it++){
	*it = 1;
    cout << *it << ' '; 
}
//不会报错,但是不会做到预想的结果,因为空的vector的begin() == end()
成员函数

vector因为支持随机访问迭代器,所以上文提到的8个操作vector都支持

除此之外,vector还支持其他几个操作

size()返回实际元素个数
max_size()返回元素个数的最大值。这通常是一个很大的数
resize()改变实际元素的个数
capacity()返回当前容量
reserve()增加容器的容量
empty()返回vector是否非空
at(i)返回vector第i个位置(从0开始)的元素的引用,这意味着我们可以直接对他进行修改,并且它会自动检查i是否会导致越界,如果越界会抛出std::out_of_range的异常
front()返回容器中第一个元素的直接引用,该函数不适用于空的vector容器
back()返回容器中最后一个元素的直接应用,该函数同样不适用于空的vector容器
data()返回一个指向容器首个元素的指针
operator[]类似于数组元素的下标访问方式,返回数组对应下标的引用,可以直接操作(包括修改)
assign(val)将val赋值给vector中的每一个元素,相当于批量赋值,可以用于初始化
push_back()在vector的尾部添加一个元素,先创建,然后拷贝移动到vector末尾
pop_back()移出vector尾部的元素,对于空的vector不适用,移除后size-1,而capacity不变
insert()在指定的位置插入一个或多个元素,插入后size+1,capacity可能不变
erase()移出一个元素或一段元素,移除后size减小,capacity不变
clear()移出所有的元素,容器大小变为 0
swap()交换两个容器的所有元素/td>
emplace()在指定的位置直接生成一个元素,大小+1
emplace_back()在vector尾部生成一个元素,由于其是在尾部直接生成而不是拷贝元素或移动,因此效率比push_back高

一些例子:

vector<int> v;
cout << v.front() << endl;//Error
cout << v.back() << endl;//Error
cout << v.size() << endl;//Output 0
v.pop_back();//Error
for (auto e : v) {//no Output
    cout << e << ' ';
}
cout << v.max_size() << endl;//Output 1073741823
v.resize(10);
cout << v.max_size() << endl;//Output 1073741823
cout << v.size() << endl;//Output 10
for (auto e : v) {//Output 0 0 0 0 0 0 0 0 0 0
    cout << e << ' ';
}

其中某些函数存在多个重载,或其调用有其独特的含义,如对参数由特定要求等,这里单独列出:

1.insert
iterator insert(iterator pos,const T& elem)	在迭代器 pos 指定的位置之前插入一个新元素elem,并返回表示新插入元素位置的迭代器。
iterator insert(iterator pos,int n,const T& elem)	在迭代器 pos 指定的位置之前插入 n 个元素 elem,并返回表示第一个新插入元素位置的迭代器。
iterator insert(iterator pos,first,last) 	在迭代器 pos 指定的位置之前,插入其他容器(不仅限于vector)中位于 [first,last) 区域的所有元素,并返回表示第一个新插入元素位置的迭代器。
iterator insert(iterator pos,initializer_list<T> initlist)	在迭代器 pos 指定的位置之前,插入初始化列表(用大括号{}括起来的多个元素,中间有逗号隔开)中所有的元素,并返回表示第一个新插入元素位置的迭代器。
                                                                                    
2.emplace()
该函数与insert很像,都是在某个位置插入元素,但是要注意这个函数一次只能插入一个,同时该函数的效率比insert单次插入要高                                                                               
iterator emplace(iterator pos,const T& elem)                                             
                                                                                    
3.erase()
iterator erase(iterator pos) 删除某个指定位置的元素,返回指向被删除元素下一个位置元素的迭代器       iterator erase(iterator begin,iterator end)删除[begin,end)范围的元素,返回指向被删除元素下一个位置元素的迭代器   
                
void show(vector<int> v) {
	for (auto c : v) {
		cout << c << ' ';
	}
	cout << endl;
}
int main() {
	vector<int> v{ 1,2,4,5 };
	v.insert(v.begin() + 2, 3);
	show(v);//1 2 3 4 5
	v.insert(v.begin(), 3, 0);
	show(v);//0 0 0 1 2 3 4 5
	array<int, 2> arr{ 6,7 };
	v.insert(v.end(), arr.begin(), arr.end());
	show(v);//0 0 0 1 2 3 4 5 6 7
	v.insert(v.end(), { 8,9 });
	show(v);//0 0 0 1 2 3 4 5 6 7 8 9
	v.emplace(v.end(), 10);
	show(v);//0 0 0 1 2 3 4 5 6 7 8 9 10
	v.erase(v.begin());
	show(v);//0 0 1 2 3 4 5 6 7 8 9 10
}                                                                    
一些小tips

虽然我们创建一个向量vector的时候,理论上这个T可以是任意类型,但是bool类型作为T是会出现问题的因为其牵扯到复杂的历史遗留问题,不做展开。

deque

deque是双端队列容器,其在某些方面与vector高度相似,其对头尾的插入为O(1),对中间位置的插入为O(n),但是其与vector有一个很大的不同在于deque 容器中存储元素并不能保证所有元素都存储到连续的内存空间中。

定义

类似的,deque的创建也有两种方式:

1.
#include<deque>
using namespace std;
deque<int> deq;
2.
#include<deque>
std::deque<int> deq;

注意,这样定义的deque容器,因为容器中没有元素,所以没有为其分配空间。当添加第一个元素(比如使用 push_back() 函数)时,deque 会自动分配内存。

初始化

与vector类似的5种初始化方式

第一种方式,初始化成员列表:
deque<int> v1{1,2,3,4,5};
第二种方式,指定元素个数(默认初始化为0)
deque<int> v2(5);//5个0
第三种方式,指定元素个数并全部初始化为1
deque<int> v3(5,1);//5个1 
第四种方式,存储已经存在的deque容器值或数组
deque<int> a(v3); //5个1
deque<int> b(v1.begin(),v1.begin()+3);//1 2 3
int arr[3] = {4,5,6};
deque<int> c(arr,arr+3);//4 5 6

也跟vector一样在某些方面有限制

deque<int> v;
for (auto it = v.begin();it != v.end();it++){
	*it = 1;
    cout << *it << ' '; 
}
//不会报错,但是不会做到预想的结果,因为空的deque的begin() == end()
成员函数

因为deque支持随机访问迭代器,所以上面对随机访问迭代器的8种操作deque都支持

除此之外,deque还支持其他几个操作

size()返回实际元素个数
max_size()返回元素个数的最大值。这通常是一个很大的数
resize()改变实际元素的个数
empty()返回deque是否非空
shrink _to_fit()将内存减少到等于当前元素实际所使用的大小
at(i)返回deque第i个位置(从0开始)的元素的引用,这意味着我们可以直接对他进行修改,并且它会自动检查i是否会导致越界,如果越界会抛出std::out_of_range的异常
front()返回容器中第一个元素的直接引用,该函数不适用于空的deque容器
back()返回容器中最后一个元素的直接应用,该函数同样不适用于空的deque容器
operator[]类似于数组元素的下标访问方式,返回数组对应下标的引用,可以直接操作(包括修改)
assign(val)将val赋值给deque中的每一个元素,相当于批量赋值,可以用于初始化
push_back()在deque的尾部添加一个元素,先创建,然后拷贝移动到deque末尾
pop_back()移出deque尾部的元素,对于空的deque不适用,移除后size-1,而capacity不变
push_front()在deque的头部添加一个元素,先创建,然后拷贝移动到deque头部
pop_front()移出deque头部的元素,对于空的deque不适用,移除后size-1,而capacity不变
insert()在指定的位置插入一个或多个元素,插入后size+1,capacity可能不变
erase()移出一个元素或一段元素,移除后size减小,capacity不变
clear()移出所有的元素,容器大小变为 0
swap()交换两个容器的所有元素/td>
emplace()在指定的位置直接生成一个元素,大小+1
emplace_back()在deque尾部生成一个元素,由于其是在尾部直接生成而不是拷贝元素或移动,因此效率比push_back高
emplace_front()在deque头部生成一个元素,由于其是在头部直接生成而不是拷贝元素或移动,因此效率比push_front高

注意,deque没有data()函数,这是deque的底层实现决定的。

由于函数的定义与重载与vector十分类似,这里不再演示

deque的底层原理

deque的底层原理,是借助这样的一个结构实现的

map是一个地址数组,存储着各个连续空间的首地址,通过这个map数组,deque 容器申请的这些分段的连续空间就能实现“整体连续”的效果。

list

双向链表容器,底层是以双向链表的形式实现的。因此,list 容器中的元素可以分散存储在内存空间里。

定义

类似的,list的创建也有两种方式:

1.
#include<list>
using namespace std;
list<int> L;
2.
#include<list>
std::list<int> L;

注意,这样定义的list容器,因为容器中没有元素,所以没有为其分配空间。当添加第一个元素(比如使用 push_back() 函数)时,list 会自动分配内存。

初始化

与vector类似的5种初始化方式

第一种方式,初始化成员列表:
list<int> v1{1,2,3,4,5};
第二种方式,指定元素个数(默认初始化为0)
list<int> v2(5);//5个0
第三种方式,指定元素个数并全部初始化为1
list<int> v3(5,1);//5个1 
第四种方式,存储已经存在的list容器值或数组
list<int> a(v3); //5个1
list<int> b(v1.begin(),v1.begin()+3);//1 2 3
int arr[3] = {4,5,6};
list<int> c(arr,arr+3);//4 5 6

也跟vector一样在某些方面有限制

list<int> v;
for (auto it = v.begin();it != v.end();it++){
	*it = 1;
    cout << *it << ' '; 
}
//不会报错,但是不会做到预想的结果,因为空的list的begin() == end()
成员函数

比起vector,array和deque,list的迭代器类型为双向迭代器,这决定了对于list的迭代器不能使用任何的比较运算符,+=、-=之类的一次跨越多步的运算符以及不能通过下标访问元素。但是其支持使用==和!=运算符

size()返回实际元素个数
max_size()返回元素个数的最大值。这通常是一个很大的数
resize()调整容器的大小
empty()返回list是否非空
front()返回容器中第一个元素的直接引用,该函数不适用于空的list容器
back()返回容器中最后一个元素的直接应用,该函数同样不适用于空的list容器
assign(val)将val赋值给list中的每一个元素,相当于批量赋值,可以用于初始化
push_back()在list的尾部添加一个元素,先创建,然后拷贝移动到list末尾
pop_back()移出list尾部的元素,对于空的list不适用,移除后size-1,而capacity不变
push_front()在list的头部添加一个元素,先创建,然后拷贝移动到list头部
pop_front()移出list头部的元素,对于空的list不适用,移除后size-1,而capacity不变
insert()在指定的位置插入一个或多个元素,插入后size+1,capacity可能不变
erase()移出一个元素或一段元素,移除后size减小,capacity不变
clear()移出所有的元素,容器大小变为 0
swap()交换两个容器的所有元素
emplace()在指定的位置直接生成一个元素,大小+1
emplace_back()在list尾部生成一个元素,由于其是在尾部直接生成而不是拷贝元素或移动,因此效率比push_back高
emplace_front()在list头部生成一个元素,由于其是在头部直接生成而不是拷贝元素或移动,因此效率比push_front高
splice()将一个 list 容器中的元素插入到另一个容器的指定位置
remove(val)删除容器中所有等于 val 的元素
remove_if()删除容器中满足条件的元素
unique()删除容器中相邻的重复元素,只保留一个
merge()合并两个事先已排好序的 list 容器,并且合并之后的 list 容器依然是有序的,如果原本的两个list无序则报错
sort()通过更改容器中元素的位置,将它们进行排序
reverse()反转容器中元素的顺序

另外,由于list底层的实现机制的原因,对list容器进行插入、删除、结合等操作的时候不会导致原有的迭代器失效,只有删除会导致当前指向的迭代器失效。

部分list添加的函数举例:

void show(list<int> v) {
	for (auto c : v) {
		cout << c << ' ';
	}
	cout << endl;
}
int main() {
	list<int> L{ 0,0,0,1,4,3 };
	show(L);//0 0 0 1 4 3
	L.remove(0);
	show(L);//1 4 3
	L.sort();
	show(L);//1 3 4 
	list<int> L2{ 4,5,6 };
	L.merge(L2);
	show(L);//1 3 4 4 5 6
	L.unique();
	show(L);//1 3 4 5 6
	L.reverse();
	show(L);//6 5 4 3 1
}

insert、erase等函数的使用方法与上文一致,这里不再重复
但是对于一些新的方法这里给出使用形式

1.splice()
splice() 成员方法的作用对象是其它 list 容器,其功能是将其它 list 容器中的元素添加到当前 list 容器中指定位置处
void splice (iterator pos, list& l);将 l 容器中存储的所有元素全部移动当前 list 容器中 pos 指明的位置处。
void splice (iterator pos, list& l, iterator it);将 l 容器中 it 指向的元素移动到当前容器中 pos 指明的位置处
void splice (iterator pos, list& l, iterator first, iterator last);将 l 容器 [first, last) 范围内所有的元素移动到当前容器 pos 指明的位置处
注意,无论使用哪种形式,添加完成后l都为空
                                                                           
2.remove_if()
remove_if(lambda)该函数接受一个lambda表达式(实际上是一个函数),满足条件的都将被删除。                        
void show(list<int> v) {
	for (auto c : v) {
		cout << c << ' ';
	}
	cout << endl;
}
int main() {
	list<int> L{ 0,0,0,1,4,3 };
	list<int> L2{ 4,5,6 };
	L.splice(L.begin(), L2);//4 5 6 0 0 0 1 4 3
	show(L);
	L.splice(L.end(), L2, L2.begin());//0 0 0 1 4 3 4
	show(L);
	L.splice(L.end(), L2, L2.begin(), L2.end());//0 0 0 1 4 3 4 5 6
	show(L);
    L.remove_if([&](int data) {return data <= 0; });//1 4 3
	show(L);
}                                                                     
list的底层原理

list的底层即双向链表

forward_list

forward_list,采用单链表构建的一种stl数据结构

定义

类似的,forward_list的创建也有两种方式:

1.
#include<forward_list>
using namespace std;
forward_list<int> L;
2.
#include<forward_list>
std::forward_list<int> L;

注意,这样定义的forward_list容器,因为容器中没有元素,所以没有为其分配空间。当添加第一个元素(比如使用 push_back() 函数)时,forward_list 会自动分配内存。

初始化

与vector类似的5种初始化方式

第一种方式,初始化成员列表:
forward_list<int> v1{1,2,3,4,5};
第二种方式,指定元素个数(默认初始化为0)
forward_list<int> v2(5);//5个0
第三种方式,指定元素个数并全部初始化为1
forward_list<int> v3(5,1);//5个1 
第四种方式,存储已经存在的forward_list容器值或数组
forward_list<int> a(v3); //5个1
forward_list<int> b(v1.begin(),v1.begin()+3);//1 2 3
int arr[3] = {4,5,6};
forward_list<int> c(arr,arr+3);//4 5 6

也跟vector一样在某些方面有限制

forward_list<int> v;
for (auto it = v.begin();it != v.end();it++){
	*it = 1;
    cout << *it << ' '; 
}
//不会报错,但是不会做到预想的结果,因为空的forward_list的begin() == end()
成员函数

因为forward_list底层采用了单链表的形式,因此forward_list的迭代器为正向迭代器,因此其对应的前面八个迭代器函数中带r的都不存在

before_begin()返回指向容器中第一个元素之前的位置的迭代器
cbefore_begin()返回指向容器中第一个元素之前的位置的迭代器,不能用于修改元素
max_size()返回元素个数的最大值。这通常是一个很大的数
resize()调整容器的大小
empty()返回list是否非空
front()返回容器中第一个元素的直接引用,该函数不适用于空的list容器
assign(val)将val赋值给list中的每一个元素,相当于批量赋值,可以用于初始化
push_front()在list的头部添加一个元素,先创建,然后拷贝移动到list头部
emplace_front()在list头部生成一个元素,由于其是在头部直接生成而不是拷贝元素或移动,因此效率比push_front高
pop_front()移出list头部的元素,对于空的list不适用
insert_after()在指定的位置插入一个元素,返回一个指向新元素的迭代器
emplace_after()/td> 在指定的位置插入一个元素,效率高于insert_after()
erase_after()删除容器中某个指定位置或区域内的所有元素
clear()移出所有的元素,容器大小变为 0
swap()交换两个容器的所有元素/td>
splice_after()将某个 forward_list 容器中指定位置或区域内的元素插入到另一个容器的指定位置之后
remove(val)删除容器中所有等于 val 的元素
remove_if()删除容器中满足条件的元素
unique()删除容器中相邻的重复元素,只保留一个
merge()合并两个事先已排好序的 list 容器,并且合并之后的 list 容器依然是有序的,如果原本的两个list无序则报错
sort()通过更改容器中元素的位置,将它们进行排序
reverse()反转容器中元素的顺序

注意没有size()函数

forward_list的底层原理

forward_list 使用的是单链表

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值