🎉个人名片:
🐼作者简介:一名乐于分享在学习道路上收获的大二在校生
🙈个人主页🎉:GOTXX
🐼个人WeChat:ILXOXVJE
🐼本文由GOTXX原创,首发CSDN🎉🎉🎉
🐵系列专栏:零基础学习C语言----- 数据结构的学习之路----C++的学习之路
🐓每日一句:如果没有特别幸运,那就请特别努力!🎉🎉🎉 ————————————————
文章目录
- 文章简介:
- 一.set的介绍
- set文档介绍总结:
- set的使用
- set原型:
- 构造函数
- 插入操作
- 删除操作
- 寻找操作
- lower_bound AND upper_bound
- 二.关联式容器
- 三.map的介绍
- map文档介绍总结
- map的使用
- map原型:
- 构造函数
- 插入操作
- 删除操作
- 寻找操作
- operator[ ]详解
- 四.multiset与multimap
文章简介:
通过阅读这篇文章,可以全面了解C++中的map与set容器,掌握它们的基本用法和常见操作,从而在实际编程中更加灵活地运用这两个容器。
一.set的介绍
文档链接:link
set文档介绍总结:
1.set是按照一定次序存储元素的容器,在set中,元素的value也标识它(value就是key,类型为T),并且每个value必须是唯一的。
2.set中的元素不能在容器中修改(元素总是const),但是可以从容器中插入或删除它们。
3.在内部,set中的元素总是按照其内部比较对象(类型比较)所指示的特定严格强弱排序准则进行排序。
4.set容器通过key访问单个元素的速度通常比unordered_set容器慢,但它们允许根据顺序对子集进行直接迭代。
5.set在底层是用二叉搜索树(红黑树)实现的。
注意:
-
与map/multimap不同,map/multimap中存储的是真正的键值对<key, value>,set中只放value,但在底层实际存放的是由<value, value>构成的键值对。
-
set中插入元素时,只需要插入value即可,不需要构造键值对。
-
set中的元素不可以重复(因此可以使用set进行去重)。
-
使用set的迭代器遍历set中的元素,可以得到有序序列。
-
set中的元素默认按照小于来比较。
-
set中查找某个元素,时间复杂度为: l o g 2 n log_2 n log2n
-
set中的底层使用二叉搜索树(红黑树)来实现。
set的使用
set原型:
第一个模板参数class T,T为存储的的数据类型;
第二个是控制比较逻辑的,默认是升序,改变逻辑为greater为降序;
第三个内存池,申请空间;
构造函数
常用构造
1.支持用容器的一个迭代器区间构造
2.支持用另一个set去构造
代码演示:
int main()
{
vector<int> v = { 11,22,1,5,2,8,6,4,9 };
list<int> lt = { 11,22,1,5,2,8,6,4,9 };
set<int> s(v.begin(), v.end()); //迭代器区间构造
set<int> s2(lt.begin(), lt.end()); //迭代器区间构造
set<int> s3(s2); //用s2去构造s3
for (auto& e : s)
{
cout << e << ' ';
}
cout << endl;
for (auto& e : s2)
{
cout << e << ' ';
}
cout << endl;
for (auto& e : s3)
{
cout << e << ' ';
}
return 0;
}
//运行结果:
1 2 4 5 6 8 9 11 22
1 2 4 5 6 8 9 11 22
1 2 4 5 6 8 9 11 22
插入操作
解析:
(1)直接插入一个val,如果set里面已经存在这个值,则返回当前的iterator,bool返回false;
如果set里面没有该val,则插入val,并返回新插入的val的iterator,bool返回true;
(2)在postition位置插入val,如果set里面没有该值,则插入,返回新插入的val的iterator;
否则返回已经存在的val的iterator;
(3)将一个迭代器区间的值插入,无返回值;
代码演示:
void settest2()
{
vector<int> v = { 11,22,1,5,2,8,6,4,9 };
set<int> s;
set<int> s1;
set<int> s2;
s.insert(20); //直接插入一个val
s1.insert(v.begin(), v.end()); //迭代器区间构造
s2.insert(s2.begin(),4); //在position位置插入一个val
for (auto& e : s)
{
cout << e << ' ';
}
cout << endl;
for (auto& e : s1)
{
cout << e << ' ';
}
cout << endl;
for (auto& e : s2)
{
cout << e << ' ';
}
cout << endl;
}
//运行结果:
20
1 2 4 5 6 8 9 11 22
4
删除操作
解析:
(1)删除position位置元素
(2)删除set中所有val,并返回删除了多少个;(set没有重复元素,返回的是1 or 0,对于multiset来讲,返回的个数可以是多个,因为multiset允许数据重复)
(3)删除一个迭代器区间
代码演示
void settest3()
{
vector<int> v = { 11,22,1,5,2,8,6,4,9 };
set<int> s(v.begin(), v.end()); //v初始化s
for (auto& e : s)
{
cout << e << ' ';
}
cout << endl;
s.erase(--s.end()); //删除第一个元素,删除迭代器位置的元素
for (auto& e : s) //end()是最后一个元素的下一个元素,所以要--;
{
cout << e << ' ';
}
cout << endl;
int ret = s.erase(11); //删除11,set里有,应该返回1
cout << ret << endl; //打印返回值
int ret2 = s.erase(100); //删除100,set里面没有,应该返回0
cout << ret2 << endl; //打印返回值
for (auto& e : s)
{
cout << e << ' ';
}
cout << endl;
s.erase(s.begin(),s.end()); //删除迭代器区间的元素
for (auto& e : s)
{
cout << e << ' ';
}
cout << endl;
}
//打印结果:
1 2 4 5 6 8 9 11 22
1 2 4 5 6 8 9 11
1
0
1 2 4 5 6 8 9
寻找操作
解析:
在set中寻找val,如果找到了返回该值的const迭代器,如果没有找到,则返回end();
代码演示:
void settest4()
{
vector<int> v = { 11,22,1,5,2,8,6,4,9 };
set<int> s(v.begin(), v.end());
for (auto& e : s)
{
cout << e << ' ';
}
cout << endl;
for (int i = 0; i < v.size(); i++) //依次遍历v中元素
{
const auto& it = s.find(v[i]); //找元素位置
s.erase(it); //删除
for (auto& e : s) //打印
{
cout << e << ' ';
}
cout << endl;
}
}
//运行结果:
1 2 4 5 6 8 9 11 22
1 2 4 5 6 8 9 22
1 2 4 5 6 8 9
2 4 5 6 8 9
2 4 6 8 9
4 6 8 9
4 6 9
4 9
9
lower_bound AND upper_bound
解析:
lower_bound:
寻找val,如果存在该元素,则返回该位置的迭代器,如果不存在,则返回比val大的那一个元素的迭代器;
upper_bound:
寻找val,如果存在该元素,则返回比val大的那一个元素的迭代器,如果不存在,也返回比val大的那一个元素的迭代器;
代码演示:
void settest5()
{
vector<int> v = { 11,22,1,5,2,8,6,4,9 };
set<int> s(v.begin(), v.end());
for (auto& e : s)
{
cout << e << ' ';
}
cout << endl;
const auto& it = s.lower_bound(5); //5存在 ,返回的是5的位置的迭代器
cout << *it << endl;
const auto& it1 = s.lower_bound(7); //7不存在 ,返回的是比7大的呢一个元素的位置的迭代器
cout << *it1 << endl;
const auto& it2 = s.upper_bound(11); //11存在 ,返回的是比11大的一个元素位置的迭代器
cout << *it2 << endl;
const auto& it3 = s.upper_bound(10); //10不存在,返回的是比10大那一个元素位置的迭代器
cout << *it3 << endl;
}
//运行结果:
1 2 4 5 6 8 9 11 22
5
8
22
11
二.关联式容器
STL中的部分容器,比如:vector、list、deque、forward_list(C++11)等,这些容器统称为序列式容器,因为其底层为线性序列的数据结构,里面存储的是元素本身。
关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是<key, value>结构的键值对,在数据检索时比序列式容器效率更高。
键值对
用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代表键值,value表示与key对应的信息。比如:现在要建立一个英汉互译的字典,那该字典中必然有英文单词与其对应的中文含义,而且,英文单词与其中文含义是一一对应的关系,即通过该应该单词,在词典中就可以找到与其对应的中文含义。
三.map的介绍
map的文档链接:link
map文档介绍总结
- map是关联容器,它按照特定的**次序(按照key来比较)**存储由键值key和值value组合而成的元素。
- 在map中,键值key通常用于排序和惟一地标识元素,而值value中存储与此键值key关联的内容。键值key和值value的类型可能不同,并且在map的内部,key与value通过成员类型value_type绑定在一起,为其取别名称为pair: typedef pair<const key, T> value_type;
- 在内部,map中的元素总是按照键值key进行比较排序的。
- map中通过键值访问单个元素的速度通常比unordered_map容器慢,但map允许根据顺序对元素进行直接迭代(即对map中的元素进行迭代时,可以得到一个有序的序列)。
- map支持下标访问符,即在[ ]中放入key,就可以找到与key对应的value。
- map通常被实现为二叉搜索树(更准确的说:平衡二叉搜索树(红黑树))。
map的使用
map原型:
构造函数
解析:
常用的就是2与3
(2)用一个迭代器区间构造
(3)用另一个map去构造
代码演示
void maptest1()
{
map<string, string> kv;
kv["hello"] = "你好";
kv["string"] = "字符串";
kv["world"] = "世界";
kv["apple"] = "苹果";
kv["red"] = "红色";
map<string, string>::iterator it = kv.begin();
while (it != kv.end())
{
cout << it->first << ':' << it->second << ' ';
++it;
}
cout << endl;
map<string, string> kv2(kv.begin(), kv.end()); //迭代器区间构造
map<string, string>::iterator it2 = kv2.begin();
while (it2 != kv2.end())
{
cout << it2->first << ':' << it2->second << ' ';
++it2;
}
cout << endl;
map<string, string> kv3(kv2); //用另一个map去构造
map<string, string>::iterator it3 = kv3.begin();
while (it3 != kv3.end())
{
cout << it3->first << ':' << it3->second << ' ';
++it3;
}
cout << endl;
}
//运行结果:
apple:苹果 hello:你好 red:红色 string:字符串 world:世界
apple:苹果 hello:你好 red:红色 string:字符串 world:世界
apple:苹果 hello:你好 red:红色 string:字符串 world:世界
插入操作
解析:
(1)直接插入一个val,如果set里面已经存在这个值,则返回当前的iterator,bool返回false;
如果set里面没有该val,则插入val,并返回新插入的val的iterator,bool返回true;
(2)在postition位置插入val,如果set里面没有该值,则插入,返回新插入的val的iterator;
否则返回已经存在的val的iterator;
(3)将一个迭代器区间的值插入,无返回值;
代码演示
void maptest2()
{
map<string, string> kv;
kv["hello"] = "你好";
kv["string"] = "字符串";
map<string, string> kv2;
kv2.insert(make_pair("yellow", "黄色")); //直接插入
kv2.insert(kv.begin(), kv.end()); //将kv迭代器区间的元素插入
kv2.insert(kv2.begin(), make_pair("red", "红色")); //在position位置插入一个元素
map<string, string>::iterator it1 = kv2.begin();
while (it1 != kv2.end())
{
cout << it1->first << ':' << it1->second << ' ';
++it1;
}
cout << endl;
}
//运行结果:
hello:你好 red:红色 string:字符串 yellow:黄色
删除操作
解析:
(1)删除position位置元素
(2)删除set中所有k,并返回删除了多少个;(map没有重复元素,返回的是1 or 0,对于multiset来讲,返回的个数可以是多个,因为multimap允许数据重复)
(3)删除一个迭代器区间
void maptest3()
{
map<string, string> kv;
kv["hello"] = "你好";
kv["string"] = "字符串";
kv["world"] = "世界";
kv["apple"] = "苹果";
kv["red"] = "红色";
map<string, string>::iterator it1 = kv.begin();
while (it1 != kv.end())
{
cout << it1->first << ':' << it1->second << ' ';
++it1;
}
cout << endl;
kv.erase("hello"); //删除hello元素
it1 = kv.begin();
while (it1 != kv.end())
{
cout << it1->first << ':' << it1->second << ' ';
++it1;
}
cout << endl;
kv.erase(kv.begin()); //删除一个迭代器位置的元素
it1 = kv.begin();
while (it1 != kv.end())
{
cout << it1->first << ':' << it1->second << ' ';
++it1;
}
cout << endl;
kv.erase(kv.begin(), kv.end()); //删除一个迭代器区间 全部删除
it1 = kv.begin();
while (it1 != kv.end())
{
cout << it1->first << ':' << it1->second << ' ';
++it1;
}
cout << endl;
}
//运行结果:
apple:苹果 hello:你好 red:红色 string:字符串 world:世界
apple:苹果 red:红色 string:字符串 world:世界
red:红色 string:字符串 world:世界
寻找操作
寻找操作与set类似,就不讲解了,这里重点详解operator[ ]的操作;
operator[ ]详解
解析:
operator[ ]调用的是insert函数类实现的(其中使用的是返回值为pair<iteraotr,bool> 的insert函数)
insert函数的功能(前面讲解过):
功能:直接插入一个val,如果set里面已经存在这个值,则返回当前的iterator,bool返回false;
如果set里面没有该val,则插入val,并返回新插入的val的iterator,bool返回true;
operator[ ]功能:
如果待插入元素(k)已经存在,则返回已经存在的这个元素的迭代器指向的pair里面第二个值的引用;
如果待插入元素不存在,则先调用insert函数插入一个k ,返回这个元素的迭代器的第二个值的引用;
代码演示
void maptest4()
{
map<string, int> kv;
string arr[] = { "hello","red","blue","hello","apple","blue" };
for (auto& e : arr)
{
kv[e]++;
}
auto it = kv.begin();
while (it!=kv.end())
{
cout << it->first << '[' << it->second << ']' << endl;
++it;
}
}
//运行结果:
apple[1]
blue[2]
hello[2]
red[1]
四.multiset与multimap
multiset与set类似,只不过它允许val值可以重复;
注意:
- multiset中再底层中存储的是<value, value>的键值对
- mtltiset的插入接口中只需要插入即可
- 与set的区别是,multiset中的元素可以重复,set是中value是唯一的
- 使用迭代器对multiset中的元素进行遍历,可以得到有序的序列
- multiset中的元素不能修改
- 在multiset中找某个元素,时间复杂度为 O ( l o g 2 N ) O(log_2 N) O(log2N)
- multiset的作用:可以对元素进行排序
举个栗子:
void multisetTest()
{
multiset<int> ms;
vector<int> v = { 19,2,3,6,3,2,8,19,8,10,6, };
for (auto& e : v)
{
ms.insert(e);
}
for (auto& e : ms)
{
cout << e << ' ';
}
}
//运行结果:
2 2 3 3 6 6 8 8 10 19 19
multimap与map类似,只不过它也是允许k值可以重复;
注意:
- multimap中的key是可以重复的;
- multimap中的元素默认将key按照小于来比较;
- multimap中没有重载operator[]操作;
- 使用时与map包含的头文件相同;
举个例子:
multimap
//multimap
void multismpapTest()
{
multimap<string, int> kv;
kv.insert(make_pair("apple", 1));
kv.insert(make_pair("apple", 2));
kv.insert(make_pair("apple", 3));
kv.insert(make_pair("apple", 4));
kv.insert(make_pair("apple", 5));
auto it = kv.begin();
while (it != kv.end())
{
cout << it->first << ':' << it->second << endl;
++it;
}
}//运行结果:
apple:1
apple:2
apple:3
apple:4
apple:5
map
//map
void multismpapTest()
{
map<string, int> kv;
kv.insert(make_pair("apple", 1));
kv.insert(make_pair("apple", 2));
kv.insert(make_pair("apple", 3));
kv.insert(make_pair("apple", 4));
kv.insert(make_pair("apple", 5));
auto it = kv.begin();
while (it != kv.end())
{
cout << it->first << ':' << it->second << endl;
++it;
}
}
//运行结果:
apple:1
本章完~