C++ 关联容器set | map | multiset | multimap
Posted WhiteShirtI
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++ 关联容器set | map | multiset | multimap相关的知识,希望对你有一定的参考价值。
前情提要
根据应用场景的不桶,STL总共实现了两种不同结构的管理式容器:树型结构与哈希结构。树型结构的关联式容器主要有四种:map、set、multimap、multiset。这四种容器的共同点是:使用平衡搜索树(即红黑树) 作为其底层结果,容器中的元素是一个有序的序列。他们都是底层都是通过<key, value>结构的键值对来存储的
C++ STL 之所以得到广泛的赞誉,也被很多人使用,不只是提供了像vector, string, list等方便的容器,更重要的是STL封装了许多复杂的数据结构算法和大量常用数据结构操作。vector封装数组,list封装了链表,map和set封装了二叉树等,在封装这些数据结构的时候,STL按照程序员的使用习惯,以成员函数方式提供的常用操作,如:插入、排序、删除、查找等。让用户在STL使用过程中,并不会感到陌生
set认识及使用
set中的key就是value,就是key=value的一种关联容器。也就是说存的内容不能一样,set具有天然的去重功能
特性:
- set容器中不能存在重复元素,底层二叉树不允许存在重复元素
- 使用set的迭代器遍历set中的元素,可以得到有序序列,底层通过中序遍历得到
- set中插入元素时,只需要插入value即可,不需要构造键值对
- set中的元素不允许修改,二叉搜索树不允许修改
模板参数
T:set存放的数据类型
Compare:set元素存储顺序,默认less,为从小到大
Alloc:set中元素空间的管理方式,使用STL提供的空间配置器管理
构造函数
- explicit set (const key_compare& comp = key_compare(),
const allocator_type& alloc = allocator_type()); - template < class InputIterator >
set (InputIteratorfirst
, InputIteratorlast
, const key_compare&comp
= key_compare(), const allocator_type&alloc
= allocator_type());
void test()
{
vector<int> vec = { 4, 3, 9, 6, 3 };
set<int> set1; //空set
set<int> set2 = { vec.begin(), vec.end()};//通过迭代器创建set容器
set<int> ::iterator it = set1.begin();//set迭代器
while (it != set1.end())
{
cout << *it << " ";
//*it = 10; error 不允许修改
++it;
}
}
测试:保存5个元素,实际保存4个元素,重复元素不保存,迭代器的遍历就是按照树的中序遍历得到的
大小容量
- size() 求set元素个数
- empty() 判断set是否为空
插入
pair<iterator,bool> insert (const value_type& val);
返回值pair<iterator,bool>插入数据的位置以及插入是否成功的数据
void test()
{
vector<int> vec = { 4, 3, 9, 6, 3 };
set<int> set2 = { vec.begin(), vec.end() };//通过迭代器创建set容器
//ret.second表示pair对象第二个数据,ret.first表示pair对象第一个数据
pair<set<int>::iterator, bool> ret = set2.insert(7);
cout << ret.second << " " << *ret.first << endl;//插入成功,pair第一个数据是新数据的迭代器
ret = set2.insert(7);
cout << ret.second << " " << *ret.first << endl;//插入失败,pair第一个数据是已经存在的数据的迭代器
}
测试:
iterator insert (iterator position, const value_type& val);
该接口并非是说在该迭代器的位置前插入数据,其实和上一个插入是一样的
void printSet(const set<int>& set)
{
for (auto& s : set)
cout << s << " ";
cout << endl;
}
void test()
{
vector<int> vec = { 4, 3, 9, 6, 3 };
set<int> set2 = { vec.begin(), vec.end() };
printSet(set2);
auto it = set2.end();
set2.insert(it, 1);
printSet(set2);
}
测试:
删除
size_type erase (const value_type& val);
返回值是删除的个数,存在则删除并返回1,不存在返回0
void erase (iterator position);
void erase (iterator first, iterator last);
void test()
{
vector<int> vec = { 4, 3, 9, 6, 3, 10, 12, 50, 1 };
set<int> set2 = { vec.begin(), vec.end() };
set<int> ::iterator it = set2.begin();
int ret = set2.erase(6);//删除成功返回1
printSet(set2);
cout << ret << endl;
ret = set2.erase(5);//删除失败返回0
printSet(set2);
cout << ret << endl;
set2.erase(it);//注意不能传非法位置:end()
printSet(set2);
set2.erase(++set2.begin(), --set2.end());//删除只剩头和尾两个数据
printSet(set2);
}
测试:
查找
iterator find (const value_type& val) const;
找到返回数据的迭代器,找不到返回set容器的end()迭代器
void test()
{
vector<int> vec = { 4, 3, 9, 6, 3, 10, 12, 50, 1 };
set<int> set2 = { vec.begin(), vec.end() };
set<int> ::iterator it = set2.begin();
auto it = set2.find(3);
cout << (it != set2.end()) << endl;
auto it = set2.find(2);
cout << (it != set2.end()) << endl;
}
测试:
size_type count (const value_type& val) const;
查看是否存在某个数据
void test()
{
vector<int> vec = { 4, 3, 9, 6, 3, 10, 12, 50, 1 };
set<int> set2 = { vec.begin(), vec.end() };
cout << set2.count(4) << endl;
cout << set2.count(40) << endl;
}
测试:
其他
set默认是一个递增的序列,我们可以手动修改成递减的序列
void printSet(const set<int, greater<int>>& set)
{
for (auto& s : set)
cout << s << " ";
cout << endl;
}
void test()
{
vector<int> vec = { 4, 3, 9, 6, 3, 10, 12, 50, 1 };
set<int, greater<int>> set2 = { vec.begin(), vec.end() };
printSet(set2);
}
测试:
map
map是STL的一个关联容器,它提供一对一的hash。自动建立key - value的对应。key 和 value可以是任意你需要的类型,包括自定义类型。
特性:
- 并且在map的内部,key与value通过成员类型value_type绑定在一起, 为其取别名称为pair
- map中的元素总是按照键值key进行比较排序的,默认为递增排序
- map支持下标访问符,即在[]中放入key,就可以找到与key对应的value
- map中的key不可以重复,但是value可以重复。不能修改key,但是可以修改value
模板参数
Key:key值的类型
T:value值的类型
构造函数
explicit map (const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type()); 空构造
template < class InputIterator>
map (InputIterator first, InputIterator last, const key_compare& comp = key_compare(), 通过迭代器创建map容器
template <class T1, class T2>
void printMap(const map<T1, T2>& m)
{
//map中的数据pair,不能直接解引用获得值
typename map<T1, T2>:: const_iterator it = m.begin();
while (it != m.end())
{
cout << it->first << "-->" << it->second << endl;
++it;
}
}
void test()
{
map<int, int> map1;//空的map容器
pair<int, int> arr[] = {pair<int ,int>(1, 3), pair<int ,int>(1, 2) ,
pair<int ,int>(3, 3) , pair<int ,int>(0, 0), pair<int ,int>(5, 5) };
map<int, int> map2(arr, arr+sizeof(arr) / sizeof(arr[0]));//通过数据创建迭代器
printMap(map2);
}
测试:key值一样不能存入。迭代器的访问底层也是一个中序遍历,按照key的递增序列的遍历
map的迭代器可以修改value,但是不可以修改key
void test()
{
pair<int, int> arr[] = { pair<int ,int>(1, 3), pair<int ,int>(1, 2) ,
pair<int ,int>(3, 3) , pair<int ,int>(0, 0), pair<int ,int>(5, 5) };
map<int, int> map1(arr, arr+sizeof(arr) / sizeof(arr[0]));
printMap(map1);
cout << "修改第一个数据的value" << endl;
auto it = map1.begin();//第一个数据
it->second = 10;
//it->first = 2; error
printMap(map1);
}
测试:
[]方括号
mapped_type& operator[] (const key_type& k);
和我们平时使用数组时使用方法类似,key相当于索引,value就是索引对应的值 map[key] == value
void test()
{
pair<int, int> arr[] = { pair<int ,int>(1, 3), pair<int ,int>(1, 2) ,
pair<int ,int>(3, 3) , pair<int ,int>(0, 0), pair<int ,int>(5, 5) };
map<int, int> map1(arr, arr + sizeof(arr) / sizeof(arr[0]));
printMap(map1);
cout << map1[1] << endl;
map1[1] = 100;
cout << map1[1] << endl;
}
测试:
但是map中的[]具有新的功能,即使key不存在,也可以通过[]来插入新的一个键值对并输出对应的value值
void test()
{
pair<int, int> arr[] = { pair<int ,int>(1, 3), pair<int ,int>(1, 2) ,
pair<int ,int>(3, 3) , pair<int ,int>(0, 0), pair<int ,int>(5, 5) };
map<int, int> map1(arr, arr + sizeof(arr) / sizeof(arr[0]));
map1[10] = 100;//插入一个key为10,value为100的数据
cout << map1[10] << endl;
printMap(map1);
}
测试:
我们通过这个接口底层实现来理解为什么可以通过[]来插入数据
其实map进行[]操作就等于进行对下面这行代码进行操作
(*((this->insert(make_pair(k,mapped_type()))).first)).second
我们来分解一下
1.先执行mapped_type(),也就是value类型的缺省值
2.执行make_pair(k,mapped_type()),创建一个键值对pair对象
3.执行insert(make_pair(k,mapped_type())), 插入这个创建好的pair对象,并返回pair<iterator,bool>,其中iterator是新插入的key对应迭代器
4.执行(this->insert(make_pair(k,mapped_type()))).first,调用返回的pair<iterator,bool>第一个元素iterator
5.执行*((this->insert(make_pair(k,mapped_type()))).first),对迭代器进行解引用获得pair对象pair<int, int>
6.执行(*((this->insert(make_pair(k,mapped_type()))).first)).second,调用pair对象的第二个元素,也就是value
如果key值已经存在,insert插入失败会返回已经存在的pair< iterator, bool>,其中iterator是已经存在的键为key的pair的迭代器
at接口
mapped_type& at (const key_type& k);
void test()
{
pair<int, int> arr[] = { pair<int ,int>(1, 3), pair<int ,int>(1, 2) ,
pair<int ,int>(3, 3) , pair<int ,int>(0, 0), pair<int ,int>(5, 5) };
map<int, int> map1(arr, arr + sizeof(arr) / sizeof(arr[0]));
//at接口和operator[]的区别就在于at执行插入对象存在时抛异常
cout << map1.at(10) << endl;
}
insert
pair<iterator,bool> insert (const value_type& val);
void test()
{
map<int, int> map1;
auto ret = map1.insert(pair<int, int>(1, 1));
cout << ret.first->first << "-->" << ret.first->second << "insert:" << ret.second << endl;
ret = map1.insert(make_pair(2, 2));
cout << ret.first->first << "-->" << ret.first->second << "insert:" << ret.second << endl;
//key值存在插入失败
ret = map1.insert(make_pair(2, 3));
cout << ret.first->first << "-->" << ret.first->second << "insert:" << ret.second << endl;
C++——map和set的简介及使用