目录
一.关联式容器
例如vector、list、deque,这些容器统称为序列式容器,其底层为线性序列的数据结构,里面存的是元素本省。
而例如map、set之类的称为关联式容器
关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是<K,V>结构的键值对,在数据检索时比序列式容器效率更高。
二、键值对
用来表示一一对应关系的结构,在这种结构中通常包含了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)
{}
};
三、树形结构的关联式容器
在STL中实现了两种管理式容器,树型结构和哈希结构,树型结构的关联式容器主要有四种,map、set、mutilmap、mutilset。这四种容器的底层都是红黑树。
3.1 set
3.1.1 set的介绍
1.set是按照一定次序存储元素的容器。
2在set内value总是唯一的。set内的值都是被const修饰的,不能修改但可以通过insert和erase进行插入删除。
3.在set内部,总是按照内部元素比较,按照升序排列。
4.set容器在通过key访问单个元素时,其访问效率不如比unordered_set容器。但是set可以根据顺序对子集进行直接迭代。
5.set的底层是由红黑树实现的。
注意:
1. 与map/multimap不同,map/multimap中存储的是真正的键值对,set中只放 value,但在底层实际存放的是由构成的键值对。
2. set中插入元素时,只需要插入value即可,不需要构造键值对。
3. set中的元素不可以重复(因此可以使用set进行去重)。
4. 使用set的迭代器遍历set中的元素,可以得到有序序列
5. set中的元素默认按照小于来比较
6. set中查找某个元素,时间复杂度为:$log_2 n$
7. set中的元素不允许修改(为什么?)
8. set中的底层使用二叉搜索树(红黑树)来实现。
3.1.2 set的使用
3.1.2.1 set的模版参数列表
T: set中存放元素的类型,实际在底层存储的键值对。
Compare:set中元素默认按照小于来比较
Alloc:set中元素空间的管理方式,使用STL提供的空间配置器管理
3.1.2.2 set的构造
第一个是构造空的set
用[first,last)区间构造set。
拷贝构造
3.1.2.3 set的迭代器
iterator begin() 返回set中起始位置元素的迭代器
iterator end() 返回set中最后一个元素后面的迭代器
const_iterator cbegin() const 返回set中起始位置元素的const迭代器
const_iterator cend() const 返回set中最后一个元素后面的const迭代器
reverse_iterator rbegin() 返回set第一个元素的反向迭代器,即end
reverse_iterator rend() 返回set最后一个元素下一个位置的反向迭代器, 即rbegin
const_reverse_iterator crbegin() const 返回set第一个元素的反向const迭代器,即cend
const_reverse_iterator crend() const 返回set最后一个元素下一个位置的反向const迭 代器,即crbegin
3.1.2.4 set的容量
bool empty ( ) const 检测set是否为空,空返回true,否则返回true
size_type size() const 返回set中有效元素的个数
3.1.2.5 set的修改操作
pair<iterator,bool> insert ( const value_type& x )
在set中插入元素x,实际插入的是<x, x>构成的键值对,如果插入成功,返回<该元素在set中的位置,true>,如果插入失败,说明x在set中已经存在,返回<x在set中的位置,false>
void erase ( iterator position ) 删除set中position位置上的元素
size_type erase ( const key_type& x ) 删除set中值为x的元素,返回删除的元素的个数
void erase ( iterator first, iterator last ) 删除set中[first, last)区间中的元素
void swap ( set<Key,Compare,Allocator>& st ); 交换set中的元素
void clear ( ) 将set中的元素清空
iterator find ( const key_type& x ) const 返回set中值为x的元素的位置
size_type count ( const key_type& x ) const 返回set中值为x的元素的个数
3.1.2.6 set的使用举例
vector<int> v = { 1,2,5,2,4,7,4,5,7,8,1,2,5,7,9,0,2,2 };
set<int> s(v.begin(),v.end());
set<int>::iterator it = s.begin();
while (it != s.end())
{
cout<<*it<<" ";
it++;
}
cout << endl;
s.erase(4);
for (auto& i : s)
{
cout<<i<<" ";
}
cout << endl;
//删除不存在元素
cout << s.erase(0) << endl;
it = s.find(1);
if (it != s.end())
{
s.erase(it);
}
cout << endl;
for (auto& i : s)
{
cout << i << " ";
}
cout << endl;
可以看到set确实是实现了排序+去重 。
3.2 map
3.2.1 map的介绍
1. map是关联容器,它按照特定的次序(按照key来比较)存储由键值key和值value组合而成的元素。
2. 在map中,键值key通常用于排序和惟一地标识元素,而值value中存储与此键值key关联的内容。键值key和值value的类型可能不同,并且在map的内部,key与value通过成员类型
value_type绑定在一起,为其取别名称为pair:
typedef pair<const key, T> value_type;
3. 在内部,map中的元素总是按照键值key进行比较排序的。
4. map中通过键值访问单个元素的速度通常比unordered_map容器慢,但map允许根据顺序
对元素进行直接迭代(即对map中的元素进行迭代时,可以得到一个有序的序列)。
5. map支持下标访问符,即在[]中放入key,就可以找到与key对应的value。
6. map通常被实现为二叉搜索树(更准确的说:平衡二叉搜索树(红黑树))。
3.2.2 map的使用
1.map的模版参数
key: 键值对中key的类型
T: 键值对中value的类型
Compare: 比较器的类型,map中的元素是按照key来比较的,缺省情况下按照小于来比 较,一般情况下(内置类型元素)该参数不需要传递,如果无法比较时(自定义类型),需要用户自己显式传递比较规则(一般情况下按照函数指针或者仿函数来传递)
Alloc:通过空间配置器来申请底层空间,不需要用户传递,除非用户不想使用标准库提供的 空间配置器
2. map的构造
3.map的迭代器
begin()和end() begin:首元素的位置,end最后一个元素的下一个位置
cbegin()和cend() 与begin和end意义相同,但cbegin和cend所指向的元素不 能修改
rbegin()和rend() 反向迭代器,rbegin在end位置,rend在begin位置,其 ++和--操作与begin和end操作移动相反
crbegin()和crend() 与rbegin和rend位置相同,操作相同,但crbegin和crend所 指向的元素不能修改
4.map 的容量与元素访问
bool empty ( ) const 检测map中的元素是否为空,是返回 true,否则返回false
size_type size() const 返回map中有效元素的个数
mapped_type& operator[] (const key_type& k) 返回去key对应的value
注意:在map对元素访问的时候,如果使用[ ]去访问不存在的元素,operator[]用默认 value与key构造键值对然后插入,返回该默认value,at()函数直接抛异常。
5.map元素的修改
pair<iterator,bool> insert ( const value_type& x )在map中插入键值对x,注意x是一个键值对,返回值也是键值对:iterator代表新插入元素的位置,bool代表释放插入成功
void erase ( iterator position ) 删除position位置上的元素
size_type erase ( const key_type& x ) 删除键值为x的元素
void erase ( iterator first, iterator last ) 删除[first, last)区间中的元素
void swap ( map<Key,T,Compare,Allocator>& mp )交换两个map中的元素
void clear ( ) 将map中的元素清空iterator find ( const key_type& x )在map中插入key为x的元素,找到返回该元素的位置的迭代器,否则返回end
const_iterator find ( const key_type& x ) const在map中插入key为x的元素,找到返回该元素的位置的const迭代器,否则返回cend
size_type count ( const key_type& x ) const返回key为x的键值在map中的个数,注意map中key是唯一的,因此该函数的返回值要么为0,要么为1,因此也可以用该函数来检测一个key是否在map中
6. map的使用举例
map<string, string> m;
m.insert(pair<string, string>("peach", "桃子"));
m.insert(make_pair("banan", "香蕉"));
cout << m.size() << endl;
for (auto& e : m)
cout << e.first << "--->" << e.second << endl;
cout << endl;
auto ret = m.insert(make_pair("peach", "桃色"));
if (ret.second)
cout << "<peach, 桃色>不在map中, 已经插入" << endl;
else
cout << "键值为peach的元素已经存在:" << ret.first->first << "--->"
<< ret.first->second << " 插入失败" << endl;
// 删除key为"apple"的元素
m.erase("apple");
if (1 == m.count("apple"))
cout << "apple还在" << endl;
else
cout << "apple被吃了" << endl;
3.3 multiset
multiset就是在set的基础上允许key的重复。
3.3.1 multiset的使用
int main()
{
int a[] = { 1,1,2,3,4,5,5,6,7,8,9,9 };
multiset<int> ms;
for (auto& i : a)
{
ms.insert(i);
}
for (auto& i : ms)
{
cout<< i <<" ";
}
cout << endl;
return 0;
}
可以看到multiset是允许key重复的。
3.4 multimap
multimap和map差不多,multimap允许重复的key。
3.4.1 multimap 的使用
multimap的使用与map类似
1. multimap中的key是可以重复的。
2. multimap中的元素默认将key按照小于来比较
3. multimap中没有重载operator[]操作。
4. 使用时与map包含的头文件相同: