【C++】STL——set/multiset 和 map/multimap的使用

这篇具有很好参考价值的文章主要介绍了【C++】STL——set/multiset 和 map/multimap的使用。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. 关联式容器

在初阶阶段,我们已经接触过STL中的部分容器

【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构
比如:vector、list、deque、forward_list(C++11)等,这些容器统称为序列式容器,因为其底层为线性序列的数据结构,里面存储的是元素本身。

而今天我们要学习的几个容器称为关联式容器,那什么是关联式容器?它与序列式容器有什么区别?

关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是<key, value>结构的键值对,在数据检索时比序列式容器效率更高。
【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构

2. 树形结构的关联式容器

根据应用场景的不同,STL总共实现了两种不同结构的关联式容器:树型结构与哈希结构。

树型结构的关联式容器主要有四种:

map、set、multimap、multiset。
这四种容器的共同点是:使用平衡二叉搜索树(即红黑树)作为其底层结构,容器中的元素是一个有序的序列。
关于平衡二叉树我们后面也会讲到,其实我们在前面几篇文章讲解搜索二叉树的时候也提到过,普通的搜索二叉树如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。
那平衡二叉树顾名思义就是在普通搜索二叉树的基础上让它变的平衡(需要对树中的结点进行调整)。
这个我们后面讲到再细说。

下面一依次介绍每一个容器。

3. set

3.1 认识set

首先我们来看一下set
【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构

还是一个类模板,有三个模板参数,但其实平时我们使用的时候一般只需要管第一个模板参数就行了。
因为后两个都是缺省值的,当然如果你想改变它底层搜索树的排序方式你可以自己传第二个比较方式的仿函数(默认是升序)。

关于set的仔细介绍大家可以去看文档
【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构
不过是英文的,大家可以借助翻译工具查看
【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构

3.1 set的使用

由于我们之前已经学了好几个STL里面的容器,所以这里对于这些容器的使用,其实对我们应该是比较轻松的。所以我这里就不会像之前那样特别仔细去一个个介绍它的接口了,很多东西相信到现在这个阶段大家看着文档都能搞懂
【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构
它提供的接口大家可以先看一下,当然其中一些都不常用。

那我们上面说到:

这几个关联式容器它们的底层结构其实就是我们之前学的二叉搜索树,只不过是平衡搜索树罢了。
那set其实就对应我们前面学的搜索二叉树的应用里面的K模型,下面要学的map就对应KV模型。

那我们接下来就来熟悉一下它的使用:

看一下它的构造函数

【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构
那我们来构造一个空的set,然后插入一些值
首先使用set要包含对于的头文件#include <set>
【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构
我们使用insert插入几个元素(这里我们看他的接口其实就没有push啥的了,一般线性的序列式容器才会有push)。
当然inset还有其它版本,不常用,大家可以自行了解。

那然后我想打印出来看看,那我们就可以使用迭代器去遍历它

【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构
但是我们看到这里打印出来是1,2,3,4。
但是我们插入了好几个值啊,并且是无序的,那这里怎么回事呢?
🆗,上面说了它的底层是平衡二叉树,那我们学过二叉搜索树其实就明白怎么回事了:
搜索二叉树一般是不能有重复值的,所以如果多次插入相同的值,后面的就会插入失败(或者可以理解成它会自动进行去重);另外我们知道二叉搜索树也叫做二叉排序树,中序遍历就可以得到一个升序序列,所以这里默认遍历打印出来就是有序了。
所以可以认为set可以实现排序+去重
那支持迭代器的话,我们就可以用范围for。
【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构

那大家思考一下我们可以修改它里面的值吗?

【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构
当然是不行的,因为我们说了它的底层是搜索树,那搜索树如果我们可以任意修改里面的值的话,修改之后还能保证它还是一棵搜索树吗?
就不一定了,因为搜索树是需要满足对于的大小规则的。
所以不能修改。

如果我们想判断一个元素在不在set对象里面

可以用find接口
【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构
【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构
但其实还可以用另一个接口——count
【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构
count是统计元素的个数,但是我们的set里面不允许重复值,所以结果只有1和0,存在就是1,不存在就是0
【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构

那剩下的其它的接口,我们这里就不介绍了,有了前面的基础,相信大家一看就会,还有的就是不怎么用的,或者现阶段我们不是很能看懂的。

4. multiset

那在<set>这个头文件里面呢,出来set,还有一个multiset

【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构
multiset叫做多重集合,multi有多种的意思嘛。
【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构
还是这些接口。

那它跟set有什么区别呢?

🆗,它跟set最大的区别就是允许里面存的key值冗余。
我们可以来看一下
【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构
可以认为它有排序,但没进行去重
那允许键值冗余的话它的插入怎么做呢?
那就接着插就行了,即使插入的是已经存在的值,那搜索树的话我们说大的值要插入到右子树,小的插入到左子树,那现在相等的话其实插入到哪边都可以,可以左边也可以右边,反正平衡二叉树嘛,插入之后会对结点进行调整使得两边平衡(我们后面会讲平衡二叉树)。

然后想给大家说一下find:

现在允许键值冗余了,那就有一个问题:
如果我们find某个值的话,跟他有多个相同的值,那find的时候查找的是哪一个?
🆗,规定呢它find的是中序遍历遇到的第一个。
它遍历的时候底层走的是中序遍历嘛,所以打印出来才是有序的。
我们可以验证一下的:
【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构
现在有4个1,我们find(1),然后从返回的迭代器位置开始,如果能把4个1都打印出来,就证明是中序的第一个1
【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构
当然我们可以用count统计每个键值的个数:
【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构

不过在实际应用中用multiset一般比较少。

5. map

5.1 认识map

map呢其实就对应搜索二叉树的KV模型,它里面存的是<key, value>结构的键值对。即用来表示具有一 一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代表键值,value表示与key对应的信息。
【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构
【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构
map的接口呢也跟set几乎差不多,有需要注意的我们后面会说。

5.2 pair

那在学习map的使用之前,我们来学一个STL里面的类/结构体模板——pair
【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构

我们来看一下SGI-STL中关于pair的定义:

template <class T1, class T2>
struct pair
{
	typedef T1 first_type;
	typedef T2 second_type;
	T1 first;
	T2 second;
	pair() 
		: first(T1())
		, second(T2())
	{}
	pair(const T1& a, const T2& b) 
		: first(a)
		, second(b)
	{}
};

pair的应用:

pair是将2个数据组合成一个数据,当需要这样的需求时就可以使用pair。
(1)STL中的map就是将key和value放在一起来保存(一般first对应key,second对应value)。
(2)另一个应用是,当一个函数需要返回2个数据的时候,可以选择pair

所以可以认为pair就是库里面提供的一个键值对的类。

5.3 map的使用

那我们看到map的insert:

【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构
它的第一个版本其实就是接收一个pair类型的对象。
【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构
key是const修饰的,不能修改(即键必须是唯一的),value可以。

那我们来写写代码吧,可以用map写一个我们之前实现的那个英汉互译的词典

map包含在头文件<map>
【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构
当然插入还可以用这个
【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构
那这个make_pair是啥呢?
他其实是库里面提供的一个函数模板
【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构
它可以帮助我们创建一个pair的对象,用它的好处是我们不需要自己去指定类型,因为模板可以自动推导类型。
然后我们来遍历打印一下:
【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构
当然我们也可以这样写:
【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构
那大家看这样可以吗?
【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构
最后一个可以插入成功吗?
【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构
不行,因为键必须是唯一的,不能重复,值可以重复。
一个键只能对应一个值,一个值可以对应多个键
【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构
也可以直接通过find键去查找值
【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构

那我们也用map再写一下统计次数的程序:

跟之前的思路一样,我就直接上代码了
【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构

对map中[]的理解

然后呢,我们发现,map呢还提供了这样一个接口

【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构
它的作用是啥呢?
【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构
首先它接收的参数是键key,如果输入的键与容器中元素的键匹配,就返回该键对应的值的引用。
所以我们刚才的统计次数还可以这样写
【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构
for循环内一行代码,就搞定了

那我们接下来就来研究一下这句代码:

【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构
其实文档上面也解释了,调用这个重载的[]就等效于执行这句代码
(*((this->insert(make_pair(k,mapped_type())).first)).second
所以这个函数大概是这样一个样子:
【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构
我们看到这里面其实去调了一下insert,那我们来仔细研究一下insert的返回值
【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构
我再来解释一下:
首先要知道插入的话呢还是有成功和失败两种情况,因为键值不允许冗余;它返回的是一个pair,第一个模板参数为迭代器,第二个为bool。
当插入成功的时候,pair的first为指向新插入元素的迭代器,second为true,当插入失败的时候(其实就是插入的键已经存在了),那它的first为容器中已存在的那个相同的等效键元素的迭代器,second为false。
所以后面这个bool其实是标识插入成功还是失败。
那我们再来看这个函数
【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构
我们来拆解一下它,要不然不好看
【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构
然后我们再来看里面这个insert
【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构
我们看到这里面insert也是使用make_pair这个函数创建一个pair对象,第一个参数就是我们[]里面放的那个键key,第二个参数传的是啥呢?
我们看到它是调了值value的类型的默认构造(之前我们说过有了模板之后内置类型就也需要有构造函数了),那我们的value是int,默认构造的值是0,所以我们第一次插入刚好它的次数就是0了。然后返回这个次数对应的value的引用,外面对它++,就正好是1。
然后后续插入相同键的话,就插入失败,不会将次数变成0,但是依然返回次数(对应pair中的second)的引用,我们从1继续往上++就行了,
当然它这上面给的有些类型是进行了typedef的,我们不太好看,不过它提供了一个表,大家可以查阅
【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构
所以我们可以将它简化一下,方便看
【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构

所以我们也可以这样玩:

【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构
插入,查找、修改啥的操作这样写
【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构

总结

1. map中的的元素是键值对
2. map中的key是唯一的,并且不能修改
3. 默认按照小于的方式对key进行比较
4. map中的元素如果用迭代器去遍历,可以得到一个有序的序列
5. map的底层为平衡搜索树(红黑树),查找效率比较高 O ( l o g 2 N ) O(log_2 N) O(log2N)
6. 支持[]操作符,operator[]中实际进行插入查找

6. multimap

那和set一样:

除了map还有multimap
【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构

我们看一下

【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构
同样的,跟map相比,multimap允许键key冗余
【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构
所以它的接口里面就没有[]

简单演示一下:

【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构
当然key和value都相同也是可以的。

然后其它的就不多说了,有需要大家可以自己查阅文档。

7. 源码

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <set>
#include <map>
#include <algorithm>
using namespace std;

void test_set1()
{
	set<int> s;
	s.insert(3);
	s.insert(1);
	s.insert(4);
	s.insert(2);
	s.insert(1);
	s.insert(2);

	int x;
	while (cin >> x)
	{
		if (s.count(x))
		{
			cout << "在" << endl;
		}
		else
		{
			cout << "不在" << endl;
		}
	}
	/*while (cin >> x)
	{
		auto ret = s.find(x);
		if (ret != s.end())
		{
			cout << "在" << endl;
		}
		else
		{
			cout << "不在" << endl;
		}
	}*/

	//for (set<int>::iterator it = s.begin(); it != s.end(); it++)
	//{
	//	cout << *it << " ";
	//}
	//cout << endl;

	//for (auto e : s)
	//{
	//	cout << e << " ";
	//}
	//cout << endl;
}
void test_set2()
{
	multiset<int> s;
	s.insert(3);
	s.insert(1);
	s.insert(4);
	s.insert(2);
	s.insert(1);
	s.insert(1);
	s.insert(1);
	s.insert(2);

	//for (auto e : s)
	//{
	//	cout << e << " ";
	//}
	//cout << endl;

	auto ret = s.find(1);
	for (auto it = ret; it != s.end(); ++it)
	{
		cout << *it << " ";
	}
	cout << endl;

	cout << s.count(1) << endl;
}

void test_map1()
{
	map<string, string> m;
	//m.insert(pair<string, string>("sort", "排序"));
	m.insert(make_pair("sort", "排序"));
	m.insert(make_pair("string", "字符串"));
	m.insert(make_pair("left", "左"));
	m.insert(make_pair("right", "右"));
	m.insert(make_pair("english", "英语"));
	m.insert(make_pair("eng", "英语"));

	m["gril"];//插入
	m["boy"] = "男孩";//插入加修改(string默认构造值为"",将它修改成男孩)
	m["sort"] = "(排序)";//修改
	cout << (m["eng"])<< endl;//查找

	//for (map<string, string>::iterator it = m.begin(); it != m.end(); it++)
	//{
	//	//cout << (*it).first << ":" << (*it).second << endl;
	//	cout << it->first << ":" << it->second << endl;

	//}
	cout << endl;
}

void test_map2()
{
	string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜",
"苹果", "香蕉", "苹果", "香蕉" };
	map<string, int> m;
	/*for (auto& e : arr)
	{
		auto ret = m.find(e);
		if (ret == m.end())
		{
			m.insert(make_pair(e, 1));
		}
		else
		{
			ret->second++;
		}
	}*/
	for (auto& e : arr)
	{
		m[e]++;
	}
	for (auto& e : m)
	{
		cout << e.first << ":" << e.second << endl;
	}
	cout << endl;
}

void test_map3()
{
	multimap<string, string> m;
	//m.insert(pair<string, string>("sort", "排序"));
	m.insert(make_pair("sort", "排序"));
	m.insert(make_pair("string", "字符串"));
	m.insert(make_pair("left", "左"));
	m.insert(make_pair("right", "右"));
	m.insert(make_pair("english", "英语"));
	m.insert(make_pair("english", "(--英语--)"));

	for (auto& e : m)
	{
		cout << e.first << ":" << e.second << endl;
	}
	cout << endl;
}

int main()
{
	test_map3();

	return 0;
}


【C++】STL——set/multiset 和 map/multimap的使用,C++,c++,开发语言,map,set,STL,数据结构文章来源地址https://www.toymoban.com/news/detail-629939.html

到了这里,关于【C++】STL——set/multiset 和 map/multimap的使用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C++进阶:详细讲解容器set与map(pair、multiset、multimap)

    C++进阶:详细讲解容器set与map(pair、multiset、multimap) 上次介绍了搜索二叉树:C++进阶:二叉搜索树介绍、模拟实现(递归迭代两版本)及其应用 为了介绍后面的AVLTree和红黑树,我们要进行一些铺垫,就是set与map的介绍啦 关联式容器和序列式容器是 C++ 中两种不同的容器类

    2024年04月12日
    浏览(40)
  • 详解map、set、multimap、multiset的使用

    ✍ 作者 : 阿润菜菜 📖 专栏 : C++ map、set、multimap、multiset是C++ STL中的四种关联容器,它们内部都使用了红黑树这种高效的平衡检索二叉树来存储数据 。它们的区别和用法如下: map是一种 键值对 容器,它可以根据键来快速查找、插入和删除值,它的键是唯一的,不能重复

    2024年02月03日
    浏览(30)
  • 【C++】map/multimap/set/multiset的经典oj例题 [ 盘点&全面解析 ] (28)

    前言 大家好吖,欢迎来到 YY 滴C++系列 ,热烈欢迎! 本章主要内容面向接触过C++的老铁 主要内容含: 欢迎订阅 YY 滴C++专栏!更多干货持续更新!以下是传送门! YY的《C++》专栏 YY的《C++11》专栏 YY的《Linux》专栏 YY的《数据结构》专栏 YY的《C语言基础》专栏 YY的《初学者易

    2024年02月04日
    浏览(44)
  • map、multimap、set、multiset讲解

    2023年07月10日
    浏览(44)
  • 【C++入门到精通】C++入门 —— set & multiset (STL)

    前面我们讲了C语言的基础知识,也了解了一些初阶数据结构,并且讲了有关C++的命名空间的一些知识点以及关于C++的缺省参数、函数重载,引用 和 内联函数也认识了什么是类和对象以及怎么去new一个 ‘对象’ ,也了解了C++中的模版,以及学习了几个STL的结构也相信大家都

    2024年02月08日
    浏览(40)
  • 详解 C++中STL的map/multimap

    ap容器中所有元素都是pair,pair中第一个元素为key(键值),起到索引作用,第二个元素为value(实值)。 同时,所有元素都会根据元素的键值自动排序。 map/multimap属于关联式容器,底层数据结构是用二叉树实现的。它的优点就是可以根据key值快速找到value值。 这里需要了解map与

    2024年02月16日
    浏览(46)
  • 【C++入门到精通】C++入门 —— map & multimap (STL)

    各位小伙伴们,在这个美好的中秋节来临之际,我衷心祝福你和你的家人度过一个幸福、团圆的时刻。愿明月的皎洁照耀你的每一天,团圆的月饼传递着我对你的思念和祝福。祝福你在中秋佳节里收获幸福与快乐,家庭和睦,心想事成。中秋快乐! 前面我们讲了C语言的基础

    2024年02月08日
    浏览(38)
  • 【数据结构与算法】C++的STL模板(迭代器iterator、容器vector、队列queue、集合set、映射map)以及算法例题

    更多算法例题链接: 【数据结构与算法】递推法和递归法解题(递归递推算法典型例题) 什么是迭代器(iterator) 迭代器(iterator)的定义: 迭代器是一种检查容器内元素并遍历元素的数据类型。 迭代器提供对一个容器中的对象的访问方法,并且定义了容器中对象的范围。 容器

    2024年04月14日
    浏览(48)
  • Google 开源库Guava详解(集合工具类)—Maps、Multisets、Multimaps

    Maps有许多很酷的实用程序,值得单独解释。 Maps.uniqueIndex(Iterable,Function)解决了一个常见的情况,即有一堆对象,每个对象都有一些唯一的属性,并希望能够根据该属性查找这些对象。 假设我们有一堆字符串,我们知道它们有唯一的长度,我们希望能够查找具有特定长度

    2024年02月03日
    浏览(43)
  • 【C++】 使用红黑树模拟实现STL中的map与set

    前面的文章我们学习了红黑树,也提到了C++STL中的map和set的底层其实就是用的红黑树来实现的(而map和set的使用我们前面也学过了)。 既然红黑树我们也学习过了,那这篇文章我们就用红黑树来简单实现一下STL中的map和set,重点是学习它的框架。 上一篇文章我们实现了红黑

    2024年02月12日
    浏览(29)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包