一.关联式容器
在初期学过的vector、list、deque、forward_list(C++11)等,底层都为线性序列的序列式容器。
关联式容器是用来存储数据的,于序列式容器不同的是,里面存储的是<key,value>结构的键值对,在数据检索时比序列式容器效率更高。
二.键值对
键值对是用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代表键值,value表示与key对应的信息。比如:现在要建立一个英汉互译的字典,那该字典中必然有英文单词与其对应的中文含义,而且,英文单词与其中文含义是一一对应的关系,即通过该单词,在词典中就可以找到与其对应的中文含义。
在SGI-STL中关于键值对的定义:
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){}
};
三.set
set介绍
1.set是按照一定次序存储元素的容器。
2.在set中,元素的value也表示它(value就是key,类型为T),并且每个value必须是唯一的。set中的元素不能再容器中修改(元素总是const),但是可以从容器中插入或删除它们。
3.在内部,set中的元素总是按照其内部比较对象(类型比较)所指示的特定严格弱排序准则进行排序。
4.set容器通过key访问单个元素的速度通常比unordered_set容器慢,但它们允许根据顺序对子集进行直接迭代。
5.set在底层是用二叉搜索树(红黑树)实现的。
6.set中插入元素时,只需要插入value即可,不需要构造键值对。
7.set中的元素不可以重复(因此可以使用set去重)。
8.set中的元素默认按照小于来比较。
9.set中查找某个元素,时间复杂度为O(logn)。
10.set中的元素不允许修改。
12.与map/multimap不同,map/multimap中存储的是真正的键值对<key, value>,set中只放value,但在底层实际存放的是由<value, value>构成的键值对,因此在set容器中插入元素时,只需要插入value即可,不需要构造键值对。
set定义
set<int> s1;//1.空的set容器set<int> s2(s1);//2.用已有的s1拷贝构造s2string str("bear");set<char> s3(str.begin(), str.end());//3.用迭代器区间构造
set使用
插入删除操作
insert(x) | 当容器中没有等价元素的时候,将元素 x 插入到 set 中 |
erase(x) | 删除值为 x 的 所有 元素,返回删除元素的个数 |
erase(pos) | 删除迭代器为 pos 的元素,要求迭代器必须合法 |
erase(first,last) | 删除迭代器在 [first,last) 范围内的所有元素 |
clear | 清空容器 |
swap | 交换两个容器的数据 |
查找操作
count(x) | 返回 set 内键为 x 的元素数量 |
find(x) | 在 set 内存在键为 x 的元素时会返回该元素的迭代器,否则返回 end() |
lower_bound(x) | 返回指向首个不小于给定键的元素的迭代器。如果不存在这样的元素,返回 end() |
upper_bound(x) | 返回指向首个大于给定键的元素的迭代器。如果不存在这样的元素,返回 end() |
empty() | 返回容器是否为空 |
size() | 返回容器内元素个数 |
迭代器
begin()/cbegin() | 返回指向首元素的迭代器,其中 *begin = front |
end()/cend() | 返回指向数组尾端占位符的迭代器,注意是没有元素的 |
rbegin()/crbegin() | 返回指向逆向数组的首元素的逆向迭代器,可以理解为正向容器的末元素 |
rend()/crend() | 返回指向逆向数组末元素后一位置的迭代器,对应容器首的前一个位置,没有元素 |
以上列出的迭代器中,含有字符 c
的为只读迭代器,你不能通过只读迭代器去修改 set
中的元素的值。如果一个 set
本身就是只读的,那么它的一般迭代器和只读迭代器完全等价。只读迭代器自 C++11 开始支持。
下列对常用的函数进行模拟:
#include<iostream>
#include<set>
using namespace std;int main()
{set<int> s1;//insert函数s1.insert(1);s1.insert(2);s1.insert(3);s1.insert(4);s1.insert(5);s1.insert(6);//遍历一,范围forfor (auto i : s1){cout << i << " ";}cout << endl;//1 2 3 4 5 6//指定元素个数cout << s1.count(1) << endl;//1个//容器大小cout << s1.size() << endl;//6//erase函数1s1.erase(6);//删除元素6//erase函数2set<int>::iterator pos = s1.find(5);if (pos != s1.end()){s1.erase(pos);}//遍历二(正向迭代器)set<int>::iterator it = s1.begin();while (it != s1.end()){cout << *it << " ";it++;}cout << endl;//1 2 3 4//clear函数s1.clear();//容器判空cout << s1.empty() << endl;//1//遍历方式三(反向迭代器)set<int>::reverse_iterator rit = s1.rbegin();while (rit != s1.rend()){cout << *rit << " ";rit++;}cout << endl;// 为空
}
multiset
multiset与set的底层与函数基本相同,不同的是multiset可以允许键值冗余,也就是说multiset容器中的元素是可以重复的
四.map
map介绍
1.map是关联式容器,它按照特定的次序(按照key来比较)存储由key和值value组合而成的元素。
2.在map中,键值key通常用于排序和唯一地标识元素,而值value中存储与此键值key关联的内容。键值key和值value的类型可能不同,并且在map内部,key与value通过成员类型valur_type绑定在一起,为其取别名称为pair: (typedef pair <const key,T> value_type);
3.在内部,map中的元素总是按照键值key进行比较排序的。
4.map中通过键值访问单个元素的速度通常比unordered_map容器慢,但map允许根据顺序对元素进行直接迭代(即对map中的元素进行迭代时,就可以找到与key对应的value)。
5.map支持下标访问,即在[]中放入key,就可以找到与key对应的value。
6.map通常被实现为二叉搜索树(更准确的说:平衡二叉搜索树(红黑树))。
map定义
map<int, int> m1;//构造一个key和value都为int的空容器map<int, int> m2(m1);//拷贝m1构造一个key和value都为int的空容器map<int, int> m3(m2.begin(), m2.end());//用迭代器构造一个key和value都为int的空容器
map使用
map插入
在map中插入,需要同时插入key和value
再来看看map插入函数的原型
pair<iterator,bool> insert (const value_type& val);
insert函数的参数显示为value_type类型,实际上value_type就是pair类型的别名
typedef pair<const Key, T> value_type;
所以,我们向map容器插入元素时,需要用key和value构造一个pair对象,然后再将pair对象作为参数传入insert函数。
调用make_pair函数模板插入
库中会提供make_pair模板:
template <class T1, class T2>
pair<T1, T2> make_pair(T1 x, T2 y)
{return (pair<T1, T2>(x, y));
}
我们只需要向make_pair函数传入key和value,该函数模板会根据传入参数类型进行自动隐式推导,最终构造并返回一个对应的pair对象。
#include<iostream>
#include<map>
using namespace std;int main()
{//map插入map<int, string> m1;m1.insert(make_pair(1, "one"));m1.insert(make_pair(2, "two"));m1.insert(make_pair(3, "three"));for (auto i : m1){cout << "<" << i.first << "," << i.second << ">";cout << endl;}cout << endl;//<1,one> <2,two> <3,three>return 0;
}
在前面我们可以看到,insert函数是有返回值的,该返回值也是一个pair对象,该pair对象第一个成员的类型是map的迭代器类型,第二个成员的类型是一个bool类型,下面介绍具体含义:
1.若带插入元素的键值key在map当中不存在,则insert函数插入成功,并返回插入后元素的迭代器和true。
2.若待插入元素的键值key在map当中已经存在,则insert函数插入失败,并返回map当中键值为key的元素迭代器和false。
map查找
map查找函数的原型为:
iterator find(const key_type& k);
该函数含义为,根据所给的key值在map中进行查找,假如找到了,那么返回对应元素的迭代器;如果没找到,那么返回最后一个元素下一个位置的正向迭代器。
#include<iostream>
#include<map>
using namespace std;int main()
{map<int, string> m1;m1.insert(make_pair(1, "one"));m1.insert(make_pair(2, "two"));m1.insert(make_pair(3, "three"));//map查找map<int, string>::iterator pos = m1.find(3);if (pos != m1.end()){cout << pos->second << endl;//three}return 0;
}
map删除
先来看看map删除函数的原型:
size_type erase(const key_type& k);void erase(ietrator posision);
根据函数原型可以看到,可以根据key删除指定元素,也可以根据迭代器删除指定元素。要是根据key删除,那么返回删除元素的个数。
#include<iostream>
#include<map>
using namespace std;int main()
{map<int, string> m1;m1.insert(make_pair(1, "one"));m1.insert(make_pair(2, "two"));m1.insert(make_pair(3, "three"));//map删除cout << m1.erase(3);//1map<int, string>::iterator pos = m1.find(1);if (pos != m1.end()){m1.erase(pos);}return 0;
}
map的[]运算符重载
map的[]运算符重载函数原型为:
mapped_type& operator[] (const key_type& k);
可以看到,[]运算符重载函数的参数是一个key值,而这个函数的返回值如下:
(*((this->insert(make_pair(k, mapped_type()))).first)).second
我们将它进行拆解分析:
1.调用insert函数插入键值对。
2.拿出从insert函数获取到的迭代器。
3.返回该迭代器位置元素的值value。
下面来看看拆解代码:
mapped_type& operator[] (const key_type& k)
{//1、调用insert函数插入键值对pair<iterator, bool> ret = insert(make_pair(k, mapped_type()));//2、拿出从insert函数获取到的迭代器iterator it = ret.first;//3、返回该迭代器位置元素的值valuereturn it->second;
}
再来看看具体使用场景:
#include <iostream>
#include <string>
#include <map>
using namespace std;int main()
{map<int, string> m1;m1.insert(make_pair(1, "one"));m1.insert(make_pair(2, "two"));m1.insert(make_pair(3, "three"));m[2] = "bear"; //修改key值为2的元素的value为bearm[4] = "four"; //插入键值对<4, "four">for (auto e : m){cout << "<" << e.first << "," << e.second << ">" << " ";}cout << endl; //<1,one> <2,bear> <3,three> <4,four>return 0;
}
总结:
1.如果k不在map中,那么先插入键值对<k,V()>,然后返回该键值对中对V对象的引用。
2.如果k在map中,则返回键值为k的元素对应V对象的引用。
map迭代器
begin | 获取容器中第一个元素的正向迭代器 |
end | 获取容器中最后一个元素下一个位置的正向迭代器 |
rbegin | 获取容器中最后一个元素的反向迭代器 |
rend | 获取容器中第一个元素前一个位置的反向迭代器 |
使用方法和set的相同。
map的其它成员函数
size | 获取容器中元素的个数 |
empty | 判断容器是否为空 |
clear | 清空容器 |
swap | 交换两个容器中的数据 |
count | 获取容器中指定key值的元素个数 |
multimap
multimap与map和multiset与set的关系几乎相同,multimap也允许键值冗余
在find函数中:
map对象的find功能为:返回值为键值为key的元素的迭代器。
multimap对象的find功能为:返回底层搜索树中序的第一个键值为key的元素的迭代器。
在count函数中:
map对象的count功能为:键值为key的元素存在则返回1,不存在则返回0(find成员函数可代替)
multimap对象的count功能为:返回键值为key的元素个数(find成员函数不可代替)。
另外,由于multimap容器允许键值冗余,调用[ ]运算符重载函数时,应该返回键值为key的哪一个元素的value的引用存在歧义,因此在multimap容器当中没有实现[ ]运算符重载函数。