STL关联式容器(map、set、multimap、multiset)
STL中的序列式容器,比如:vector、list、deque、forward_list(C++11)等,其底层为线性序列的数据结构,里面存储的是元素本身
STL中的关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是<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)
{}
};
树形结构的关联式容器:
根据应用场景的不同,STL总共实现了两类不同结构的管理式容器:树型结构与哈希结构。
树型结构的关联式容器主要有四种:map、set、multimap、multiset
共同点是:使用平衡搜索树(即红黑树)作为其底层结果,容器中的元素是一个有序的序列
set
- set是按照一定次序存储元素的容器
- 在set中,元素的value也标识它(value就是key,类型为T),并且每个value必须是唯一的。set中的元素不能在容器中修改(元素总是const),但是可以从容器中插入或删除它们。
- 在内部,set中的元素总是按照其内部比较对象(类型比较)所指示的特定严格弱排序准则进行排序。
- set容器通过key访问单个元素的速度通常比unordered_set容器慢,但它们允许根据顺序对子集进行直接迭代。
- set在底层是用二叉搜索树(红黑树)实现的
- 与map/multimap不同,map/multimap中存储的是真正的键值对<key, value>,set中只放value,但在底层实际存放的是由<value, value>构成的键值对。
- set中插入元素时,只需要插入value即可,不需要构造键值对。
- set中的元素不可以重复(因此可以使用set进行去重)。
- 使用set的迭代器遍历set中的元素,可以得到有序序列
- set中的元素默认按照小于来比较
- set中查找某个元素,时间复杂度为:log2(N)
- set中的元素不允许修改
- set中的底层使用二叉搜索树(红黑树)来实现
应用
1>构造一个set类型的变量
std::set<int> s;
insert() : 插入一个元素
第一个insert返回的是一个pair类型的变量,其包含两个成员,第一个成员的类型是该map迭代器的类型,另一个成员的类型是bool
若map没有新插入的元素,那么插入成功,返回的是插入节点的迭代器以及true;
若该元素在map中已经存在,那么插入会失败,返回的是该元素已经存在节点的迭代器,以及false;
//构造一个int类型的set
std::set<int> s;
//插入元素
s.insert(4);
s.insert(5);
s.insert(1);
s.insert(3);
//测试insert()的返回值
pair<set<int>::iterator, bool> ret;
ret = s.insert(2);//set中没有2这个元素,插入成功,返回插入节点的迭代器和true
ret = s.insert(2);//set中已经存在了2,插入失败,返回的是元素4的迭代器和false
该insert插入的是一段迭代器区间
//定义一段vector,类型也是int
vector<int> v;
v.push_back(10);
v.push_back(20);
v.push_back(30);
//用insert向set s中插入该vector的全部
s.insert(v.begin(), v.end());
利用迭代器访问元素
//定义一个对应类型的迭代器,指向s的起始
std::set<int>::iterator it = s.begin();
//当it没有指向s的最后一个元素的后一个元素时,访问
while (it != s.end())
{
cout << *it << " ";
++it; //迭代器自增,指向下一个元素
}
cout << endl;
erase() :
//1.用find查找1这个元素,并用迭代器it获取返回值,然后进行删除
set<int>::iterator it = s.find(500);
if (it != s.end()) //查询成功,则表示元素存在,删除该元素
s.erase(it);
else //若查询失败,表示不存在,对it解引用会是NULL
cout << "没有找到" << endl;
//2.直接删除一个key值
s.erase(it); //该语句在循环外时,有3就删除,没有3就报错
s.erase(3); //有3就删除,没有3就不删
//3.删除一段迭代器区间
s.erase(s.begin(), s.end());
find() : 用来查找一个元素,若查找到,则返回该元素对应的迭代器,否则返回最后一个元素的下一个元素
在删除的时候用过(同上)
it = s.find(3); //O(logN) 推荐使用
//it = std::find(s.begin(), s.end(), 3); //O(N) :适于所有迭代器
if (it != s.end())
{
cout << "找到了" << endl;
s.erase(it);
}
else
cout << "没有找到" << endl;
count()
统计一个元素的个数,在set中用于判断一个元素是否存在;因为无法插入相同的值,所以在set/map中与find的作用一样
emplace()
emplace和insert都是向set中插入一个元素,但效率上看,emplace的效率要高一点。insert是先构造好,然后又经过一次拷贝构造放入set中;而emplace是直接在set中进行构造
multiset
-
查找关键字在不在
-
排序,不查重;不支持operator[]
std::multiset<int> ms; //multi多 排序,不查重;不支持operator[]
ms.insert(3);
ms.insert(3);
ms.insert(1);
ms.insert(5);
ms.insert(4);
ms.insert(3);
ms.insert(2);
ms.insert(3);
auto mit = ms.find(3); //找到一个继续找,可找到所有的3,返回的是中序的第一个3
if (mit != ms.end())
{
cout << "找到了" << endl;
while (*mit == 3)
{
cout << *mit << endl;
++mit;
}
}
for (auto e : ms)
{
cout << e << " ";
}
cout << endl;
}
map
- map是关联式容器,它按照特定的次序(按照key来比较)存储由键值key和值value组合而成的元素。
- 在map中,键值key通常用于排序和惟一地标识元素,而值value中存储与此键值key关联的内容。键值key和值value的类型可能不同,并且在map的内部,key与value通过成员类型value_type绑定在一起,为其取别名称为pair:
typedef pair value_type; - 在内部,map中的元素总是按照键值key进行比较排序的。
- map中通过键值访问单个元素的速度通常比unordered_map容器慢,但map允许根据顺序对元素进行直接迭代(即对map中的元素进行迭代时,可以得到一个有序的序列)。
- map支持下标访问符,即在[]中放入key,就可以找到与key对应的value。
- map通常被实现为二叉搜索树(更准确的说:平衡二叉搜索树(红黑树))。
【map->通过关键字查找映射信息value】:
map是一种key(键),value(值)的形式,用来保存键和值组成的集合,键必须是唯一的,但值可以不唯一。里面的元素可以根据键进行自动排序,由于map是key_value的形式,所以map里的所有元素都是pair类型。pair里面的first被称为key(键),second被称为value(值)
pair是一个有两个模板参数的结构体,分别是first(key)、second(value)
template<class K, class V>
struct pair
{
K first;
V second;
//构造函数
};
创建pair类型的方法
pair<string,int>("桃子",5);
make_pair("香蕉",1);
pair<string,int> p = {"苹果",3};
make_pair
template<class K, class V>
inline std::pair<K, V> make_pair(const K& k, const V& v)
{
return std::pair<K, V>(k, v);
}
pair与make_pair的比较
pair:是将两个数据合成一个数据,当有这样的需求时就要用到pair,就是map。函数要返回连个数据的时候要用到pair,pair是一个结构体,因为两个成员变量用的是struct而不是class。
make_pair:可使用pair的构造函数,也可使用make_pair来生成需要的pair。一般来说,在需要pair的时候我们用make_pair来生成pair的对象很方便,但由于pair可以进行隐示的类型转换局带来一些问题,如下:
std::pair<int, float>(2, 2.2);
std::make_pair(2, 2.2);
第一个是float,第二个就自动匹配成了double
应用:
插入:insert()
1>.插入一个pair
std::map<std::string, std::string> dict;
dict.insert(std::pair<std::string, std::string>("sort", "排序"));
dict.insert(std::make_pair("string", "字符串"));
2>.插入一个value_type
dict.insert(std::map<std::string, int>::value_type("change", 1));
3>. 利用数组下标进行插入,这里的K值需要是int
std::map<std::string, std::string> dict;
dict["pair"];
dict["key"] = "关键字";
dict["pair"] = "剩余";
删除:erase()
1>.迭代器删除
std::map<std::string, int>::iterator it = dict.find("字符串");
if (it != m.end())
dict.erase(it);
2>.key值删除
dict.erase("关键字");
查找:find()
iterator find ( const key_type& x );
const_iterator find ( const key_type& x ) const;
用来查找一个元素,查找成功,返回这个元素的迭代器,否则返回最后一个元素的下一个元素(end)
count
统计个数,不允许出现重复的Key值,所以find()作用类似
size_typecount ( const key_type& x ) const;
和set中的count使用功能相同
operator[]
T&operator[] ( const key_type& x );
operator[]比较常用, 实际进行插入查找。如果map中有这个x,则它就把这个x所对应的value的引用返回。如果map中没有这个x的话,则它会调用insert(pair<K,V>(k,V())),将k和V的缺省值对应起来插入后并返回这个value的引用。
multimap
与 map 类似,所不同的是它允许重复键。这个属性使得 multimap 比预想的要更有用:比如在电话簿中相同的人可以有两个以上电话号码,文件系统中可以将多个符号链接映射到相同的物理文件,或DNS服务器可以将几个URLs映射到相同的IP地址。在这些场合,你可以象下面这样:
// 举例:伪码
multimap <string, string> phonebook;
phonebook.insert("Lily","88345678"); // 家里电话
phonebook.insert("Lily","17712345678"); // 单位电话
phonebook.insert("Lily"," 15887654321"); // 移动电话
multimap中的接口可以参考map,功能都是类似的。
#include <map>
#include <string>
void TestMultimap1()
{
multimap<string, string> m;
m.insert(make_pair("李逵", "黑旋风"));
m.insert(make_pair("林冲", "豹子头"));
m.insert(make_pair("鲁达", "花和尚"));
// 尝试插入key相同的元素
m.insert(make_pair("李逵", "铁牛"));
cout << m.size() << endl;
for (auto& e : m)
cout << "<" << e.first << "," << e.second << ">" << endl;
// key为李逵的元素有多少个
cout << m.count("李逵") << endl;
return 0;
}
void TestMultimap2()
{
multimap<int, int> m;
for (int i = 0; i < 10; ++i)
m.insert(pair<int, int>(i, i));
for (auto& e : m)
cout << e.first << "--->" << e.second << endl;
cout << endl;
// 返回m中大于等于5的第一个元素
auto it = m.lower_bound(5);
cout << it->first << "--->" << it->second << endl;
// 返回m中大于5的元素
it = m.upper_bound(5);
cout << it->first << "--->" << it->second << endl;
}
注意:
- multimap中的key是可以重复的。
- multimap中的元素默认将key按照小于来比较
- multimap中没有重载operator[]操作
- 使用时与map包含的头文件相同