在遍历中使用 iterator/reverse_iterator 进行 Erase 的使用方法
本文遵循“署名-非商业用途-保持一致”创作公用协议

众所周知,在使用迭代器遍历 STL 容器时,须要特别留意是否在循环中改动了迭代器而导致迭代器失效的情形。以下我来总结一下在对各种容器进行正向和反向遍历过程中删除元素时,正确更新迭代器的使用方法。本文源代码:https://code.csdn.net/snippets/173595

首先,要明确使用正向迭代器(iterator)进行反向遍历是错误的使用方法,要不干嘛要有反向迭代器呢(reverse_iterator)。其次,依据容器的特性,遍历删除操作的使用方法能够分为两组,第一组是 list 和 vector,第二组是 map 和 set。

接下来,看看详细怎么个使用方法。

第一种情形:正向遍历删除元素

对 list 和 vector 来说,它们的 erase 函数会返回下一个迭代器,因此在遍历时,仅仅须要 it = c.erase(it); 就可以。

对 map 和 set 来说,它们的 erase 函数返回的 void,而在进行 erase 之后,当前迭代器会失效,无法再用于获取下一个迭代器。因此须要 erase 之前就获取指向下一个元素的迭代器。如:

tmpIt = it;
++it;
c.erase(tmpIt);

利用后缀++操作符的特性(先创建副本,然后再递增迭代器,然后返回副本)上面的三行代码能够简化为一行:

c.erase(it++);

list 正向遍历删除元素演示样例(vector 使用方法同样):

    list<int>::iterator it;
for (it = l.begin(); it != l.end();)
{
if (0 == (*it) % 2) {
it = l.erase(it);
}
else {
++it;
}
}

map 正向遍历删除元素演示样例(set 使用方法同样)

    map<int, int>::iterator mit;
for (mit = m.begin(); mit != m.end();)
{
if (0 == mit->first % 2) {
m.erase(mit++);
}
else {
++mit;
}
}

另外一种情形,反向遍历删除元素关于正向/反向迭代器的关系,请參考《Effective STL》,在这里我仅仅说明一点,两者相差一个元素,从一个反向迭代器获得相应的正向迭代器须要使用 base() 方法。例如以下图所看到的:ri 是指向元素3的反向迭代器,而 i 是 ri.base() 所得到的正想迭代器。

由于全部的 erase 函数都仅仅接受正向迭代器 iterator,所以在进行反向遍历删除元素时,首先须要将 reverse_iterator 转换为 iterator,然后再考虑更新迭代器的问题。

先来分析怎样将 reverse_iterator 转换为 iterator。如上图所看到的,我们想要删除元素3,而 ri.base() 所得到的正向迭代器 i 指向的事实上 4 了,因而为了正确地删除元素 3,须要将ri往前(反向的)挪一个位置。也就是说,这一步的删除使用方法应为:

c.erase((++rit).base());

或:(想想为什么?,但这个使用方法不具备可移植性,由于有些 STL 实现不同意改动函数返回的指针)

c.erase(--(rit.base();

然后,我们来分析迭代器更新的问题。
对 list/vector 来说,由于的 erase 能够返回一个有效的正向迭代器,因而仅仅须要将返回的正向迭代器转换为反向迭代器就可以。

对 map/set 来说,由于在进行删除操作 l.erase((++rit).base()) 时,迭代器已经更新过了,真是一举两得啊。从这里也能够看出,使用这样的先递增后 base() 的转换删除法,代码更清晰。

至此,理论分析完成,以下我们来看详细的实例。

list 反向遍历删除元素演示样例(vector 使用方法同样):

    // erase with reverse_iterator
list<int>::reverse_iterator rit;
for (rit = l.rbegin(); rit != l.rend();)
{
if (0 == (*rit) % 2) {
rit = list<int>::reverse_iterator(l.erase((++rit).base()));
}
else {
++rit;
}
}

map 反向遍历删除元素演示样例(set 使用方法同样):

    // erase with reverse_iterator
map<int, int>::reverse_iterator rit;
for (rit = m.rbegin(); rit != m.rend();)
{
if (0 == rit->first % 2) {
m.erase((++rit).base());
}
else {
++rit;
}
}

OK,删除使用方法相信大家都明确了,可是,可是,引起迭代器失效的操作还有插入操作呀,相信聪明的你一定能够举一反三正确更新迭代器~~

在遍历中使用 iterator/reverse_iterator 进行 Erase 的使用方法的更多相关文章

  1. 如何在遍历中使用 iterator/reverse_iterator 删除元素

    如何在遍历中使用 iterator/reverse_iterator 删除元素 罗朝辉 (http://www.cnblogs.com/kesalin/) 本文遵循“署名-非商业用途-保持一致”创作公 ...

  2. JavaSE中Collection集合框架学习笔记(3)——遍历对象的Iterator和收集对象后的排序

    前言:暑期应该开始了,因为小区对面的小学这两天早上都没有像以往那样一到七八点钟就人声喧闹.车水马龙. 前两篇文章介绍了Collection框架的主要接口和常用类,例如List.Set.Queue,和A ...

  3. 【前端】【javascript】es6中的遍历器接口Iterator

    好久没发文章啦-.-为了证明我还活着,我决定从笔记里面抓一篇还算不乱比较像文章的发出来... 这些笔记是我在学es6的时候断断续续记录的,最近会一份一份整理陆陆续续发出来,顺便也自己再看一遍.我学习e ...

  4. C++ STL中的 iterator 和 const_iterator

    我们在C++中使用STL的容器时,经常会用到迭代器.使用迭代器可以很方便的进行容器元素遍历和修改等操作. 近日,在使用Visual Studio 2015编程的时候发现,set的迭代器直接就是cons ...

  5. 【二叉树遍历模版】前序遍历&&中序遍历&&后序遍历&&层次遍历&&Root->Right->Left遍历

    [二叉树遍历模版]前序遍历     1.递归实现 test.cpp: 12345678910111213141516171819202122232425262728293031323334353637 ...

  6. RocksDB笔记 - Compaction中的Iterator

    Compaction中的Iterator 一般来说,Compaction的Input涉及两层数据的合并,对于涉及到的每一层数据: 如果是level-0,对level-0的每一个sstable文件建立一 ...

  7. Java集合类中的Iterator和ListIterator的区别

    注意:内容来自网络他人文章! 最近看到集合类,知道凡是实现了Collection接口的集合类,都有一个Iterator方法,用于返回一个实现了Iterator接口的对象,用于遍历集合:(Iterato ...

  8. java中的Iterator接口

    Iterator接口 Iterator接口也是Java集合框架的成员,但它与Collection系列.Map系列的集合不一样:Collection系列集合.Map系列集合主要用于盛装其他对象,而Ite ...

  9. Java循环遍历中直接修改遍历对象

    Java 循环遍历中直接修改遍历对象如下,会报异常: for (ShopBaseInfo sp: sourceList) { if(sp.getId()==5){ sourceList.remove( ...

随机推荐

  1. Caused by: org.springframework.beans.factory.BeanCreationException

    1.错误原因 2014-7-13 17:36:57 org.apache.jasper.compiler.TldLocationsCache tldScanJar 信息: At least one J ...

  2. nginx随着passenger构造ruby on rails页

    1.备份nginx简介 cp /opt/nginx/html/nginx.conf /opt/nginx/html/nginx.conf.bak 2.编者nginx简介 server { listen ...

  3. 第三篇——第二部分——第三文 配置SQL Server镜像——域环境

    原文:第三篇--第二部分--第三文 配置SQL Server镜像--域环境 原文出处:http://blog.csdn.net/dba_huangzj/article/details/28904503 ...

  4. 16.怎样自学Struts2之Struts2异常处理[视频]

    16.怎样自学Struts2之Struts2异常处理[视频] 之前写了一篇"打算做一个视频教程探讨怎样自学计算机相关的技术",优酷上传不了,仅仅好传到百度云上: http://pa ...

  5. BZOJ 2435 NOI2011 道路建设 BFS/DFS

    标题效果:给定一个树(直接将树.不要贪图生成树图!).寻找每条边权值*分差的两侧之间 BFS水必须是能 竟DFS能够住...系统堆栈可能有些不够,我们可以使用内联汇编手册中大型系统堆栈 详见代码 这个 ...

  6. HDU 2112 HDU Today (Dijkstra算法)

    HDU Today Time Limit: 15000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total ...

  7. 最大流量dinci模板

    我们知道.增广路径EK时间是在充电算法的O(n*m^2).找到最短增广路径的时间复杂度为O(m*n^2).这样的时间复杂度主要是寻找扩充道路. 这里也有一个演示Dinci算法,使用BFS层次结构图,然 ...

  8. 金蝶K3管理软件PDA条码解决方式,盘点机与金蝶K3无缝对接

    申明:以上文字为"武汉汉码科技有限公司"原创,转载时务必注明出处. 技术分享,沟通你我,共同进步!www.hanma-scan.com 原帖:http://www.hanma-sc ...

  9. 3D空间中的AABB(轴向平行包围盒, Aixe align bounding box)的求法

    引言 在前面的一篇文章中讲述了怎样通过模型的顶点来求的模型的包围球,而且还讲述了基本包围体除了包围球之外,还有AABB包围盒.在这一章,将讲述怎样依据模型的坐标求得它的AABB盒. 表示方法 AABB ...

  10. 介绍 Microservice

    介绍 Microservice 这篇文章转自我的 Github blog 一天我司招财猫姐(HR 大人)问我,你给我解释一下 Microservice 是什么吧.故成此文.一切都是从一个创业公司开始的 ...