前言
map 和 set 是 STL 中的关联式容器,底层是平衡二叉搜索树,map( key 模型),set( key,val 模型)。
一、set
1.insert
关于 set 的 insert 函数,使用起来也很简单,首先是 set 所要包含的头文件:
#include<set>
其次就是函数的调用了,根据模板参数传递参数,比如:
set<int> s;
s.insert(2);
s.insert(2);
s.insert(1);
s.insert(5);
s.insert(3);
输出的话,使用范围for或者迭代器遍历都行,如下:
int main()
{
set<int> s;
s.insert(2);
s.insert(2);
s.insert(1);
s.insert(5);
s.insert(3);
set<int>::iterator it = s.begin();
while (it != s.end())
{
cout << *it << " ";
++it;
}
cout << endl;
return 0;
}
可以看到输出结果是这样的,因为set的底层是平衡二叉搜索树,所以是不允许重复的 key 存在的,所以可以理解为set 会做数据的去重操作,而 set 对于数据默认是做升序处理。
那么 set 是否可以修改数据呢?来进行如下尝试:
int main()
{
set<int> s;
s.insert(2);
s.insert(2);
s.insert(1);
s.insert(5);
s.insert(3);
set<int>::iterator it = s.begin();
while (it != s.end())
{
*it += 1; //key += 1
cout << *it << " ";
++it;
}
cout << endl;
return 0;
}
答案是不行的,因为底层是平衡二叉搜索树,如果要修改某一个key时,就有可能会破坏原来搜索的规则,我们知道二叉搜索树 root 的 key 比左子树的大,比右子树的小,当我们要修改一个节点的 key 时,可能导致不满足这个规则,所以 set 是不允许修改key的值的。
2.count
set 的 count 函数就是在容器中搜索与传入的参数等效的元素,并返回匹配的数量,而因为 set 中的所有元素都是唯一的,所以函数只能返回 1(如果找到元素)或 0(否则)。
比如:
int main()
{
set<int> s;
s.insert(2);
s.insert(2);
s.insert(1);
s.insert(5);
s.insert(3);
//set {1,2,3,5}
//找1
cout << s.count(1) << endl;
//找10
cout << s.count(10) << endl;
return 0;
}
结果显而易见。
二、multiset
1.insert
multiset 的 insert 和 set 的唯一区别就是它的 insert 不去重,即允许重复的 key 存在,比如:
int main()
{
multiset<int> s;
s.insert(2);
s.insert(2);
s.insert(1);
s.insert(5);
s.insert(3);
multiset<int>::iterator it = s.begin();
while (it != s.end())
{
cout << *it << " ";
++it;
}
cout << endl;
return 0;
}
其他的没有区别,一样是不允许修改 key 的值。
2.find
find 函数会返回查找到的 key 的迭代器,因为 multiset 允许存在重复的 key,所以在调用 find 函数时会返回中序的第一个 key 的迭代器,比如:
即在 find(1) 时,会返回多个 1 中的中序的第一个 1。
3.count
multiset 的 count 返回的是 key 出现的次数,即:
int main()
{
multiset<int> s;
s.insert(2);
s.insert(2);
s.insert(1);
s.insert(5);
s.insert(3);
cout << s.count(2) << endl;;
cout << s.count(5) << endl;
return 0;
}
因为允许重复的 key 存在,所以这样的结果也是理所当然。
三、map
1.insert
map 的值存的是键值对,即<key,val>,所以不能像set直接插入,要借助类 pair<const key_type,mapped_type>,
pair中存在两个成员变量,一个是 first,即 key,一个是 second,即 val。
使用匿名对象 pair 进行插入:
map<string, int> countMap;
countMap.insert(pair<string,int>("苹果",1));
使用 make_pair 进行插入:
make_pair 是一个函数模板,通过其可以构造出一个 pair,而使用 make_pair 的好处就是不需要像使用 pair 那样显示的传递参数的类型,可以直接传递参数而自动进行类型的推导:
map<string, int> countMap;
countMap.insert(make_pair("苹果",1));
map 中依然是不允许有重复的key存在的。
2.operator[ ]
map 的方括号重载是很有用的,对于统计某个 key 出现的次数是很好用的,operator[ ] 大致的执行流程是,通过参数传递一个 key ,最终会返回该 key 的键值对对应的 val 的引用,而 val 如果不赋值,默认会调用其默认构造,可以通过赋值来改变相应 key 的 val 值,做到对同一个 key 的统计,比如:
int main()
{
string str[] = { "apple","apple","banana","pear","pear" };
map<string, int> countMap;
for (const auto& e : str)
{
countMap[e]++;
}
for (const auto& e : countMap)
{
cout << e.first << ": " << e.second << endl;
}
return 0;
}
四、multimap
和 map 没有太大的区别,也就是支持重复的 key 存在,比如:
int main()
{
multimap<string, string> mmap;
mmap.insert(make_pair("string","字符串1"));
mmap.insert(make_pair("string","字符串2"));
auto it = mmap.begin();
cout << it->first << ": " << it->second << endl;
++it;
cout << it->first << ": " << it->second << endl;
return 0;
}
总结
温故而知新。