STL map&set用法详解

本博客已弃用,当时存在一些小细节错误后期也不再修改了

欢迎来我的新博客

        

STL标准模板库中用得最多的两个容器map和set是我们每个人必须熟练掌握的,不论是在Oj、技术面还是工作中如果能非常熟练的运用这两个容器,那么写起代码来必然是事半功倍的,反之可能会在某些问题上寸步难行。map和set都是通过红黑树作为底层实现的模板容器,效率非常之高,使用得当可以实现各种巧妙功能,几乎可以胜任我们所有需要用到容器的情况。下面进入正题:

set

头文件:<set>

模板参数列表:

    

    set的模板参数如图,第一个参数是你要存放数据或者说元素的类型参数,第二个参数是仿函数,并且默认缺省参数为less<T>,第三个参数是空间配置器参数,默认缺省参数为allocator<T>(空间配置器这一项目前一般不会去认为的写参数,会使用默认的缺省参数,故不做太多说明,以后的文章里说)。

常用接口:

1.迭代器(iterator)接口

begin():显然,返回存有set的头迭代器

end():显然,返回尾迭代器

在此稍微解释一下,因为set和map都是以红黑树作为底层框架实现,而红黑树本身是一棵搜索树,其中序本身就是有序的,故头迭代器的位置就是红黑树的最左节点,尾迭代器就是STL中红黑树的header节点,想详细了解header节点是怎么回事的话得自己去看STL源码中的红黑树,但是你只要知道这里的尾迭代器是在红黑树的最右节点的右孩子,这样做的巧妙之处在反向迭代器的实现中就能体现(其实和<list>中的处理是相似的)。

rbegin():返回反向迭代器的头,也就是正向的尾

rend():返回反向迭代器的尾,就是正向的头

cbegin(),cend(),crbegin(),crend()是上述四个迭代器的静态版本

2.容量(capacity)接口

empty():判断容器是否为空

size():返回容器中元素的个数

max_size():用于检查set中是否能放得下一定数量的元素(个人感觉用得不多),如图:

3.调整(Modifiers)接口

insert():

用法如上三种

    (1)形参为要插入的值或数据,即值插,返回值为pair类型,其中封装有迭代器(pair.first)和bool类型(pair.second)的返回值。pair.first为迭代器,如果插入成功则该迭代器为插入成功位置的迭代器,且pair.second值为true;否则,该迭代器为插入值已经存在的那个节点对应的迭代器,并且pair.second值为false(这个接口常常可以用来去重)

      (2)形参为迭代器(即插入位置)与要插入的值或数据。返回值为迭代器,返回的迭代器是什么迭代器和(1)同理。

      (3)形参为内置迭代器类型,是一个迭代器区间。也就是说你可以用vector,list等封装好的内置迭代器的来插入(事实上数组也可以哦,如下)

 

erase():

用法如上三种

看完上面明白insert接口咋用了,难道erase接口不是一看就懂吗?╮(๑•́ ₃•̀๑)╭

swap():形参是一个set(常用用法),大家一看也都明白,这个接口就是交换两个set(个人感觉这个接口用得也不算太多,感觉要交换set的情况并不多啊)

clear():清空容器。这个大家肯定一看就知道啦,所有STL容器这个接口都是一样的设计。

4.操作(Operations,cplusplus.com是这样给它分类的,中文也只能这样翻译吧,意会。。。。)接口

find():

用法如上,形参为要查找的值,返回值类型是迭代器类型,很容易知道,若是找到了那么返回这个值所在位置的迭代器,否则返回前面提到的end迭代器,即header节点

count():

 

用法如上,形参也是要查找的值或者元素,返回值是一个与size_t类似的size_type(size_type和size_t具体有什么区别个人感觉也算个大部人都不知道盲区了,同学们自行查阅吧),若该值或者说元素存在那么返回一个非0正数,否则返回0,动脑的同学就会想了,根据红黑树的搜索树的特性存在返回1不就行了嘛,咱们用bool作返回值不久行了吗?何必这样返回一个非0正数呢?

其实这是因为STL中还有一个multiset,根据名字就可以知道,multiset里的元素是允许冗余的,multiset::count这个函数如果要找的元素存在的话,那么它不仅会告诉你存在,还会告诉你出现了几次,因此set为了保持STL的设计一致性,故把该接口也改为了size_type而不是bool,从这个细节是不是能看出STL这样工业级的代码在设计时的精细之处,当然说这个主要是提醒各位也该去了解multiset的用法,虽然几乎和set是一样的,但是就怕有同学都不知道有这个东西。下面就是multiset::count的用法

ps:multiset的头文件也是<set>

 

upper(lower)_bound()

 

用法如上,得到你某个值的下界和上界的迭代器,看看下面的代码你就明白了

输出结果

同学们可以去试试在上面例子中在lower_bound与upper_bound函数中分别写35与75试试,这样似乎更能体现这个函数的意义。

set的用法弄明白了,map真的就是看一眼就知道每个接口怎么用了

Map

头文件:<map>

模板参数列表如下

其中

 

模板参数唯一的区别就是模板参数多了一个,因为set是一种k(key)模型,而map则是<k,v>(<key,value>)模型(如字典),故模板参数会多一个。其它几个模板参数和set是一样的。

常用接口:

        迭代器接口、容量接口、调整接口、操作接口由于STL设计的优点,首先map和set的这些接口函数名(上面set中提到的)是完全一模一样的,(用法上只不过map是插入pair<k,v>或用迭代器,其他接口稍看参数列表就知道怎么去用了),并且同样对应的multimap,所以只要你会用set那么map也就基本会用了。为啥只是基本会用呢?在这里就要说说map的一个特别巧妙并且特别好用的接口了,讲完这个我们也就讲完了map和set。

 

这个接口值得我们好好看看源码实现

如下

没错就是这一句,而且是个返回值,短小精悍。现在我尝试用我自己的话概括一下(各位同学要是没懂就自己去好好拆拆这句代码做了件什么事):

        形参为k,用调用insert函数插入一个pair<k,v_default>,这里的v_default是模板参数中的第二个类型调用其缺省构造函数,比如内置类型int就是0,string类就是""(空字符串),然后把这个pair进行插入,然后我们容易知道(看上面set)insert有一个重载会返回一个pair<iterator,bool>,然把这个pair的first取出来,那么我们又得到了一个迭代器,由insert用法可知若k已存在那么这个迭代器就是已经存在的那个位置,否则就是新插入的位置的迭代器。然后对这个迭代器解引用,我们就得到了迭代器解引用后的那个位置的数据,即pair<k,v>,最后返回v的引用,因此我们不经可以得到v还可以修改v,若不赋值并且k又是一个新插入的值的话那么v就是前面说的调用缺省构造函数的所得到的值。

        肯定很多人不咋懂= =,但是不管怎么样,先学会用吧,用法如下:

        

输出结果

       各位不妨思考一下,什么样的自定义类型一般不要作为pair<K,V>中的V放入map中呢?

       答:当然是没有定义缺省构造函数的自定义类型,如果把这种类型作为V,那么一旦调用map[],程序就一定崩。

       上面对于multiset与multimap没有详说,因为接口和set与map非常相似,基本用法是一样的,并且这两个的使用频率同样也是相当高的。

      最后说一点,也是很重要的一点!!C++11后有以哈希表为底层的unordered_map与unordered_set也是相当相当重要!!划重点!不要觉得自己知道了map和set就满足了,无论是在OJ还是在面试中,unordered_map与unordered_set都是经常被问到的,因为这两个容器在某些情况比map和set更高效。这两个容器的接口和map,set非常相似,一定要去熟练他们的用法。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值