系列文章目录
文章目录
- 系列文章目录
- 前言
- 1、关联式容器与序列式容器
-
- [1.1 键值对](#1.1 键值对)
- 2、set的介绍
- 3、multiset的介绍
-
- [3.1 接口count与容器multiset](#3.1 接口count与容器multiset)
- 4、map的介绍
-
- [4.1 接口insert](#4.1 接口insert)
- [4.2 operator[]和at](#4.2 operator[]和at)
- 5、multimap的介绍
前言
根据应用场景的不桶,STL总共实现了两种不同结构的管理式容器:树型结构与哈希结构。树型结构的关联式容器主要有四种:map、set、multimap、multiset。这四种容器的共同点是:使用平衡搜索树(即红黑树)作为其底层结果,容器中的元素是一个有序的序列。
1、关联式容器与序列式容器
vector、list、deque、forward_list(C++11)等,这些容器统称为序列式容器,因为其底层为线性序列的数据结构,里面存储的是元素本身。
而关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是<key, value>结构的键值对,在数据检索时比序列式容器效率更高。 (插入删除只需挪动指针指向,无需挪动数据,查找时间logN)
关联式容器有两种,一种是map、set、multimap、multiset采用树形结构,他们的底层都是红黑树,另一种是哈希结构。
1.1 键值对
用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代表键值,value表示与key对应的信息。比如:现在要建立一个英汉互译的字典,那该字典中必然有英文单词与其对应的中文含义,而且,英文单词与其中文含义是一一对应的关系,即通过该应该单词,在词典中就可以找到与其对应的中文含义。
SGI-STL中关于键值对的定义:
cpp
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)
{}
};
2、set的介绍
-
set是关联式容器,它表面上只存放value,实际底层中存放的是由<value,value>组成的键值对。
-
set中的元素不可以重复(因此可以使用set进行去重)。
-
使用set的迭代器遍历set中的元素,可以得到有序序列(排序+去重)。
-
为了保证元素的唯一性,set中的元素均为const,所以并不能对元素进行修改,但可以进行插入删除。
-
set中的底层使用二叉搜索树(红黑树)来实现。
-
set调用find将采用中序遍历。
cpp
void test1()
{
set<int> s1;
s1.insert(3);
s1.insert(1);
s1.insert(2);
s1.insert(4);
s1.insert(3);
s1.insert(5);
for (auto e : s1)
{
cout << e << " ";
}
cout << endl;
}
//结果
1 2 3 4 5
3、multiset的介绍
与set的区别为 :允许键值冗余。
使用multiset的迭代器遍历multiset中的元素,可以得到有序非去重序列(排序)。
cpp
void test1()
{
multiset<int> s2;
s2.insert(3);
s2.insert(1);
s2.insert(2);
s2.insert(4);
s2.insert(3);
s2.insert(5);
for (auto e : s2)
{
cout << e << " ";
}
cout << endl;
}
//结果
1 2 3 3 4 5
3.1 接口count与容器multiset
set的count和find的作用一样,都是用于查找set中是否存在某个元素。
其实count是为了容器multiset设计的,该容器允许插入重复的元素,此时count会返回红黑树中被搜索元素的个数。
cpp
void test1()
{
multiset<int> s1;
s2.insert(3);
s2.insert(1);
s2.insert(2);
s2.insert(4);
s2.insert(3);
s2.insert(5);
cout << s1.count(3) << endl;
}
//结果
2
4、map的介绍
map是关联式容器,根据特定的存储顺序,用于存储由键值及其映射值组合的元素。
可以看到Alloc中有一个键值对pair,这个pair是一个key/value结构的struct模板类。这个类将一对键值耦合在一起,所以,map的存储方式是通过在搜索二叉树中存储键值对pair,而搜索二叉树的k/v模型是在节点中存储key和value,并不相同。pair的结构:
cpp
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)
{}
};
4.1 接口insert
使用map的迭代器遍历map中的元素,可以得到有序序列(排序+去重)。
cpp
void test2()
{
map<string, string> dict;
dict.insert(pair<string, string>("left", "左边"));
dict.insert(pair<string, string>("right", "右边"));
dict.insert(make_pair("sort", "排序"));
dict.insert(make_pair("string", "字符串"));
dict.insert(make_pair("count", "计数"));
dict.insert(make_pair("count", "计数"));
map<string, string>::iterator it = dict.begin();
while (it != dict.end())
{
cout << it->first << ":" << it->second << endl;
++it;
}
}
//结果
count:计数
left:左边
right:右边
sort:排序
string:字符串
make_pair是一个函数模板:
cpp
template <class T1,class T2>
pair<T1,T2> make_pair (T1 x, T2 y)
{
return ( pair<T1,T2>(x,y) );
}
为了保证元素的唯一性,map中的元素均为const,所以并不能对元素进行修改,但可以进行插入删除。
4.2 operator[]和at
使用map统计每个字符出现个数
cpp
void test3()
{
string arr[] = { "西瓜", "西瓜", "苹果", "苹果", "香蕉", "梨" };
map<string, int> countMap;
for (auto& e : arr)
{
auto ret = countMap.find(e);
if (ret == countMap.end())
{
countMap.insert(make_pair(e, 1));
}
else
{
ret->second++;
}
}
for (auto e : countMap)
{
cout << e.first << ":" << e.second << endl;
}
}
调用insert函数,函数的第二个参数为value类型的缺省值,调用默认构造。
返回值是pair<iterator,bool>,pair.first 表示迭代器 ,解引用就为pair数据 ,pair数据取second就为value。
cpp
void test4()
{
string arr[] = { "西瓜", "西瓜", "苹果", "苹果", "香蕉", "梨" };
map<string, int> countMap;
for (auto& e : arr)
{
countMap[e]++;
}
for (auto e : countMap)
{
cout << e.first << ":" << e.second << endl;
}
}
cpp
V& operator[](const K& key)
{
pair<iterator, bool> ret = insert(make_pair(key, V()));
return ret.first->second;
}
【operator[]的作用】:
-
插入:插入 left,第二个给缺省值,而string缺省值为空串。
-
修改:由于插入的left已经存在,所以插入失败,并寻找之前已经存在的left对应的迭代器,把left迭代器的返回值 value修改为左边。
-
插入+修改:插入right,第二个缺省值为空串,并把返回值 value修改为右边。
-
查找:直接返回对应的value值即可。
5、multimap的介绍
与map的区别:允许键值冗余。
使用multimap的迭代器遍历multiset中的元素,可以得到有序非去重序列(排序)。
cpp
void test5()
{
multimap<string, string> dict;
dict.insert(pair<string, string>("left", "左边"));
dict.insert(pair<string, string>("right", "右边"));
dict.insert(make_pair("sort", "排序"));
dict.insert(make_pair("string", "字符串"));
dict.insert(make_pair("count", "计数"));
dict.insert(make_pair("count", "数字"));
multimap<string, string>::iterator it = dict.begin();
while (it != dict.end())
{
cout << it->first << ":" << it->second << endl;
++it;
}
}
//结果
count:计数
count:数字
left:左边
right:右边
sort:排序
string:字符串
但是multimap并没有operator[],因为在map中,key和value是一对一的关系,在multimap中,key和value是一对多的关系,所以没办法判断当前key值对应哪一个value。