1. 关联式容器
-
序列式容器里面存储的是元素本身,其底层为线性序列的数据结构。比如:vector,list,deque,forward_list(C++11)等。
-
关联式容器里面存储的是<key, value>结构的键值对,在数据检索时比序列式容器效率更高。比如:set、map、unordered_set、unordered_map等。
注意: C++STL当中的stack、queue和priority_queue属于容器适配器,它们默认使用的基础容器分别是deque和vector
2. 树形结构与哈希结构
根据应用场景的不同,C++STL总共实现了两种不同结构的关联式容器:树型结构和哈希结构。
关联式容器 | 容器结构 | 底层实现 |
set、map、multiset、multimap | 树型结构 | 平衡搜索树(红黑树) |
unordered_set、unordered_map、unordered_multiset、unordered_multimap | 哈希结构 | 哈希表 |
3. 键值对->pair类型
键值对是用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代表键值,value表示与key对应的信息。
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)
{}
};
- pair用了两个模板参数,set和map中某些函数的返回值是pair
4. set的使用
#include <iostream>
#include <set>
using namespace std;
void test_set()
{
int a[] = { 1, 2, 1, 6, 3, 8, 5 };
set<int> s(a, a + sizeof(a) / sizeof(int));
//set<int, greater<int>> s(a, a + sizeof(a) / sizeof(int));
// 排序 + 去重
set<int>::iterator it = s.begin();
while (it != s.end())
{
cout << *it << " ";
++it;
}
cout << endl;
s.erase(2);
for (auto e : s)
{
cout << e << " ";
}
cout << endl;
set<int>::iterator pos = s.find(3);
if (pos != s.end())
{
s.erase(pos);
}
for (auto e : s)
{
cout << e << " ";
}
cout << endl;
cout << s.count(3) << endl;
cout << s.count(8) << endl;
}
int main() {
test_set();
return 0;
}
- 在定义set时,不传第二个类型less<int>,默认比较方式是小于
- set在底层是用平衡搜索树(红黑树/二插搜索树)实现的,所以在set当中查找某个元素的时间复杂度为logN。
- set中的count函数,本来是统计这个元素在set中出现的次数,但是二叉树的值不能有重复的
所以它的返回不是1就是0
4.1 lower_bound && upper_bound
#include <iostream>
//#include <functional>
#include <map>
#include <set>
#include <string>
using namespace std;
void test_set2()
{
std::set<int> myset;
std::set<int>::iterator itlow, itup;
for (int i = 1; i < 10; i++)
myset.insert(i * 10); // 10 20 30 40 50 60 70 80 90
itlow = myset.lower_bound(25); // >= val
itup = myset.upper_bound(60); // > val //
cout << "[" << *itlow << "," << *itup << ")" << endl;
// 删掉一段迭代器区间(左闭右开)
myset.erase(itlow, itup); // 10 20 70 80 90
std::cout << "myset contains:";
for (std::set<int>::iterator it = myset.begin(); it != myset.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
}
int main() {
test_set2();
return 0;
}
- 一个迭代器的区间通常是左闭右开的
- lower_bound(val)返回这个set中第一个>=val值的跌迭代器
- upper_bound(val)返回这个set中第一个>val值的跌迭代器
4.2 equal_range
#include <iostream>
//#include <functional>
#include <map>
#include <set>
#include <string>
using namespace std;
void test_set2()
{
std::set<int> myset;
for (int i = 1; i <= 5; i++) {
myset.insert(i * 10); // myset: 10 20 30 40 50
}
//std::pair<std::set<int>::const_iterator, std::set<int>::const_iterator> ret;
pair<set<int>::const_iterator, set<int>::const_iterator> ret;
ret = myset.equal_range(40); // xx <= val < yy
std::cout << "the lower bound points to: " << *ret.first << '\n';
std::cout << "the upper bound points to: " << *ret.second << '\n';
}
int main() {
test_set2();
return 0;
}
- 返回一个范围的边界,该范围包含容器中所有等效于val的元素
4.3 了解multiset(允许出现重复节点)
#include <iostream>
//#include <functional>
#include <map>
#include <set>
#include <string>
using namespace std;
void test_set3()
{
int a[] = { 3,1, 2, 1, 6, 3, 8,3, 5,3 };
multiset<int> s(a, a + sizeof(a) / sizeof(int));
// 排序
for (auto e : s)
{
cout << e << " ";
}
cout << endl;
cout << s.count(3) << endl;
// find时,如果有多个值,返回中序的第一个
auto pos = s.find(3);
while (pos != s.end())
{
cout << *pos << " ";
++pos;
}
cout << endl;
// 删除所有的3
s.erase(3);
for (auto e : s)
{
cout << e << " ";
}
cout << endl;
pos = s.find(1);
if (pos != s.end())
{
s.erase(pos);
}
for (auto e : s)
{
cout << e << " ";
}
cout << endl;
}
int main() {
test_set3();
return 0;
}
- multiset和set类似,本质也是一颗二插搜索树,
- 不同点在:这棵树允许出现重复节点
- multiset的find函数,如果有多个值,返回中序的第一个
- multiset的erase函数,如果删除的是val,那么删除这棵二叉树中所有的val
5. map的使用
#include <iostream>
#include <functional>
#include <map>
#include <set>
using namespace std;
void test_map1()
{
map<string, string> dict;
dict.insert(pair<string, string>("sort", "排序"));
typedef pair<string, string> DictKV;
dict.insert(DictKV("string", "字符串"));
dict.insert(make_pair("left", "左边"));// 推荐
//map<string, string>::iterator it = dict.begin();
auto it = dict.begin();
while (it != dict.end())
{
//cout << (*it).first << (*it).second <<endl;
cout << it->first << it->second << endl;
++it;
}
cout << endl;
for (const auto& kv : dict)
{
cout << kv.first << ":" << kv.second << endl;
}
cout << endl;
}
int main() {
test_map1();
return 0;
}
- map的底层是一种映射,是一种键值对
- map的begin函数返回的是一个迭代器,但这个迭代器是一个pair类型的指针,
pair的成员是first(Key)和second(Value),是键值对的关系 - make_pair函数返回的是pair类型,就像是构造了一个pair
- map在底层是用平衡搜索树(红黑树)实现的,所以在map当中查找某个元素的时间复杂度为logN
5.1 []运算符
#include<iostream>
#include <map>
#include <string>
using namespace std;
void test_map1()
{
string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
map<string, int> countMap;
for (auto& str : arr)
{
// 1、str不在countMap中,插入pair(str, int()),然后在对返回次数++
// 2、str在countMap中,返回value(次数)的引用,次数++;
countMap[str]++;
}
map<string, int>::iterator it = countMap.begin();
while (it != countMap.end())
{
cout << it->first << ":" << it->second << endl;
++it;
}
cout << endl;
map<string, string> dict;
dict.insert(make_pair("sort", "排序"));
dict["insert"];
dict["insert"] = "插入";
dict["left"] = "左边";
multimap<string, string> mdict;
mdict.insert(make_pair("sort", "排序"));
mdict.insert(make_pair("left", "左边"));
mdict.insert(make_pair("left", "左边"));
mdict.insert(make_pair("left", "剩余"));
}
int main()
{
test_map1();
return 0;
}
- map中有这个key,返回value的引用,此时map[]的作用是查找,修改value
- map中没有这个key,就会插入一个pair(key,V(),调用它的默认构造函数,返回vaule的引用
此时map[]的作用是插入,修改value - map不存在重复键值对
5.2 map的[]运算符底层实现
insert的返回值
- key已经在map中,返回pair(key_iterator,false);
- key不在map中,返回pair(new_key_itetator,true);
- insert的返回值类型,是为了[]运算符做准备
简单实现一下
- []的底层是调用的insert接口
5.3 了解multimap(允许出现重复键值对)
- multimap除了允许出现重复键值对之外,其他的和map相同
- 由于multimap容器允许键值冗余,调用[ ]运算符重载函数时,应该返回键值为key的哪一个元素的value的引用存在歧义,因此在multimap容器当中没有实现[ ]运算符重载函数