一、关联式容器
在初阶阶段,我们已经接触过STL中的部分容器,比如:vector、list、deque、forward_list(C++11)等,这些容器统称为序列式容器,因为其底层为线性序列的数据结构,里面 、存储的是元素本身。
那什么是关联式容器?它与序列式容器有什么区别? 关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是结构的键值对,在数据检索时比序列式容器效率更高。
二、键值对
用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代表键值,value表示与key对应的信息。
上次我们二叉搜索树讲到kv模型中key-val就是一个键值对,
在举个例子:如果键值对对应的是名字与拼音,以map的使用为例
#include<iostream>
#include<map>
using namespace std;
int main()
{
map<string, string> dirt;
dirt.insert(pair<string, string>("小明","xiaoming"));
dirt.insert(make_pair("小李", "xiaoli"));
pair<string, string> kv("张三", "zhangsan");
dirt.insert(kv);
return 0;
}
三、set的使用
3.1set的介绍
- set是按照一定次序存储元素的容器
- 在set中,元素的value也标识它(value就是key,类型为T),并且每个value必须是唯一的。set中的元素不能在容器中修改(元素总是const),但是可以从容器中插入或删除它们。
- 在内部,set中的元素总是按照其内部比较对象(类型比较)所指示的特定严格弱排序准则进行 排序。
- set容器通过key访问单个元素的速度通常比unordered_set容器慢,但它们允许根据顺序对 子集进行直接迭代。
- set在底层是用二叉搜索树(红黑树)实现的。
T是set中存放元素的类型,底层存储用<value,value>的键值对
Compare:set中元素默认按照小于来比较
Alloc:set中元素空间的管理方式,使用STL提供的空间配置器管理
3.2set的构造
跟大部分的容器类似,这些我就不细讲了
函数声明 | 功能介绍 |
---|---|
set (const Compare& comp = Compare(), const Allocator& = Allocator() ); | 构造空的set |
set (InputIterator first, InputIterator last, const Compare& comp = Compare(), const Allocator& = Allocator() ); | 用[first,last)区间中的元素构造set |
set ( const set& x); | 拷贝构造 |
3.3set的迭代器
函数声明 | 功能 |
---|---|
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 |
#include<iostream>
#include<map>
#include<set>
using namespace std;
void test1()
{
set<int> s;
s.insert(5);
s.insert(1);
s.insert(6);
s.insert(3);
s.insert(4);
s.insert(5);
s.insert(1);
set<int>::iterator it = s.begin();
while (it!=s.end())
{
cout << *it << " ";
it++;
}
cout << endl;
}
int main()
{
test1();
return 0;
}
set的遍历是有序的且会去重
3.4set的容量
函数声明 | 功能介绍 |
---|---|
bool empty ( ) const | 检测set是否为空,空返回true,否则返回false |
size_type size() const | 返回set中有效元素的个数 |
3.5set的修改操作
函数声明 | 功能介绍 |
---|---|
pair<iterator,bool> insert ( const value_type& x ) | 在set中插入元素x,实际插入的是构成的 键值对,如果插入成功,返回该元素在set中的 位置,true>,如果插入失败,说明x在set中已经 存在,返回在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的元素的个数 |
void test2()
{
set<int> s;
s.insert(5);
s.insert(1);
s.insert(6);
s.insert(3);
s.insert(4);
auto start = s.lower_bound(3); // >=val 左闭
cout << *start << endl;
auto finish = s.upper_bound(5); //>val 右开
cout << *finish << endl;
//s.erase(start, finish);//区间删除
while (start != finish)//左闭右开
{
cout << *start << " ";
++start;
}
cout << endl;
for (auto e : s)
{
cout << e << " ";
}
cout << endl;
}
3.6使用演示
void test1()
{
set<int> s;
s.insert(5);
s.insert(1);
s.insert(6);
s.insert(3);
s.insert(4);
s.insert(5);
s.insert(1);
set<int>::iterator it = s.begin();
while (it!=s.end())
{
cout << *it << " ";
it++;
}
cout << endl;
set<int>::iterator pos = s.find(5);
if (pos != s.end())
{
s.erase(pos);
}
for (auto e : s)
{
cout << e << " ";
}
cout << endl;
s.erase(3);//存在则删除
s.erase(10);//该数若不存在则不做处理
for (auto e : s)
{
cout << e << " ";
}
}
multiset的使用
multiset与set的区别是:multiset允许重复个元素
由此引申出一些成员函数的区别
count: 返回的是元素x的个数
erase:删除序列中所有的x
find:返回序列中的第一个x
四、map的使用
1. map是关联容器,它按照特定的次序(按照key来比较)存储由键值key和值value组合而成的元 素。
2. 在map中,键值key通常用于排序和惟一地标识元素,而值value中存储与此键值key关联的 内容。键值key和值value的类型可能不同,并且在map的内部,key与value通过成员类型 value_type绑定在一起,为其取别名称为pair: typedef pair value_type;
3. 在内部,map中的元素总是按照键值key进行比较排序的。
4. map中通过键值访问单个元素的速度通常比unordered_map容器慢,但map允许根据顺序 对元素进行直接迭代(即对map中的元素进行迭代时,可以得到一个有序的序列)。
5. map支持下标访问符,即在[]中放入key,就可以找到与key对应的value。
6. map通常被实现为二叉搜索树(更准确的说:平衡二叉搜索树(红黑树))。
4.1map的成员函数
构造
函数声明 | 功能介绍 |
---|---|
map() | 构造一个map |
迭代器
容量
元素访问
mapped_type& operator[] (const key_type& k) | 返回去key对应的value |
修改及查找
insert
在map中插入键值对x,注意x是一个键值对,返回值也是键值对:iterator代表新插入 元素的位置,bool代表释放插入成功
erase
(1)删除pos位置上的元素
(2)删除键值为k的元素
(3)删除[first,last)区间中的元素
swap
交换两个map中的元素
clear
将map中的元素清空
find
(1)在map中插入key为x的元素,找到返回该元 素的位置的迭代器,否则返回end
(2) 在map中插入key为x的元素,找到返回该元 素的位置的const迭代器,否则返回cend
count
返回key为x的键值在map中的个数,注意 map中key是唯一的,因此该函数的返回值 要么为0,要么为1,因此也可以用该函数来 检测一个key是否在map中
4.2map的使用
map的两个值是存到一个pair对象中的first(key)、second(val)
map的key(pair的first)不支持修改,val(pair的second)可以
pair的first有const修饰
void test_map1()
{
map<string, string> dict;
dict.insert(pair<string, string>("sort", "排序"));
pair<string, string>kv("string", "字符串");
dict.insert(kv);
dict.insert({ "apple","苹果" });//C++11(构造函数)多参数的隐式类型转换
//C++98常用
dict.insert(make_pair("sort", "排序"));
map<string, string>::iterator it = dict.begin();
while (it!=dict.end())
{
//cout << (*it).first << " " << (*it).second << endl;
cout << it->first << " " << it->second << endl;
it++;
}
}
map也有multi版本
4.3额外内容
来看一下下面的代码
计算一个字符串数组中各字符串的出现次数
void test_map3()
{
string arr[] = { "苹果","西瓜","香蕉","草莓","梨","苹果","香蕉","草莓","西瓜","苹果","西瓜" };
//以水果为key(pair的first),出现次数为val(pair的second)
map<string, int> countMap;
for (auto& e : arr)
{
map<string, int>::iterator it = countMap.find(e);
if (it != countMap.end())//出现过则second++
{
it->second++;
}
else//第一次出现就往map里存水果名字,出现次数是1
{
countMap.insert(make_pair(e, 1));
}
}
for (auto& kv : countMap)
{
cout << kv.first << ":" << kv.second << endl;
}
cout << endl;
}
再看一下下面的
void test_map3()
{
string arr[] = { "苹果","西瓜","香蕉","草莓","梨","苹果","香蕉","草莓","西瓜","苹果","西瓜" };
//以水果为key(pair的first),出现次数为val(pair的second)
map<string, int> countMap;
for (auto& e : arr)
{
countMap[e]++;
}
for (auto& kv : countMap)
{
cout << kv.first << ":" << kv.second << endl;
}
cout << endl;
}
一句代码就解决了存字符串和出现次数的问题
operator[]通过这个key去返回val的引用
如果map中没有这个key?operator是借助map.insert来实现的
看一下insert的返回值,pair<iterator,bool>(这个pair不是map的pair),可以理解为类pair里有两成员变量
上面是对insert的返回值的first解引用再取其second
如果这个值(要插入的值)没有,插入成功(bool为真),pair的first(iterator)是新插入元素所在节点的迭代器;
如果这个值以及存在(插入失败),bool为假,pair的first(iterator)指向已有此元素的迭代器
简化一下
V& operator[](const K& key)
{
pair<iterator, bool> ret = insert(make_pair(key, V()));
return ret.first->second;
}
operator[]的功能
1.插入
2.查找
3.修改
4.查找+修改
void test_map2()
{
map<string, string> dict;
dict.insert(make_pair("sort", "排序"));
dict.insert(make_pair("string", "字符串"));
dict.insert(make_pair("sort", "xxx"));
dict["left"];//插入
cout << dict["sort"] << endl;//查找
dict["sort"] = "abc";//修改
dict["right"] = "右边";//插入+修改
for (auto& kv : dict)
{
cout <<kv.first<< ":"<<kv.second<<endl;
}
cout << endl;
}