2021-03-24

C++ list链表(列表)迭代器 erase失效问题

list不常用,因为它不能随机访问,但最近由于需要频繁的删除数据,所以不得已必须要使用list提高效率。
删除数据后会发生迭代器失效问题,对于网上的科普有很多,原因也就是那一个问题,但是解决的方法确始终没有说清楚,让人感觉怪怪的,总感觉跳了节点。
经过尝试,找到一种比较好的的一遍能处理所有符合条件得遍历方法:
由于我们常规是从头节点往尾节点遍历,我这里也就只说这种情况。其他自己脑补

重点:由于从头往后遍历,所以从逻辑上就不建议保存本节点的前一节点迭代器,这是最重要的地方,不能保存前一节点迭代器。这是因为如果第一个节点就满足删除要求,那么你求前一节点迭代器就没有意义,一步都走不下去就报错的。

所以通过上面的分析,也就直接可以得出结论:不适用for循环遍历list
解决方法:使用while循环遍历list对象!!!划重点,要用while循环遍历

上代码:

#include <list>
std::list<int> tempList={1,2,3,3,4,5,6,7};
//重点来了!
auto i = tempList.begin();
while( i!=tempList.end() )
{
	if(*i == 3)//这里写你的判断条件,总之是要删除i这个元素的条件
		i = tempList.erase(i);
	else
		i++;
}
//大功告成

分析:
首先:用自动推导auto得到list的初始迭代器;之后做循环条件,如果不是尾指针,就进入循环。
满足条件就删除这个i对应的迭代器指向的元素,注意,list.erase(迭代器i)这个函数是有返回值的。返回你删除的元素的下一个元素的迭代器!,也就是没删之前 i 的下一个迭代器。
如果不满足条件也要自增i++
反正不管你满足不满足,我都要让i指向后面的那个元素的迭代器。有的小伙伴可能有这样错误的写法

auto i = tempList.begin();
while( i!=tempList.end() )
{
    if(*i == 3)//这里写你的判断条件,总之是要删除i这个元素的条件
		{
		   tempList.erase(i);
		   i++;//这里一定会报错,因为删除i元素后,i就是野指针
		       //而且链表储存本来就不连续,所以,野指针自增是无意义的,一定会出错。
		}
	else
		i++;
}

这样写是正确的:

auto i = tempList.begin();
while( i!=tempList.end() )
{
    if(*i == 3)//这里写你的判断条件,总之是要删除i这个元素的条件
		{
		i++;
		auto nextIt = i;
		i--;
		tempList.erase(i);//其实这里直接用erase的返回值就可以了,不用我们自己手动存储一个下节点迭代器。这也是这个函数有返回值的初衷
	    i = nextIt  ;
		}
	else
		i++;
}

我这里写的代码已经是比较简单且很容易理解的了,逻辑上也没有什么曲折的地方,就是很普通的代码,但是如果独立思考这个迭代器失效的问题,想用循环写出一次遍历就把所有满足条件的节点剔除掉,如果用for循环非常的头疼。
这里还要说两件事情:
第一就是可以使用for(;;){}写出死循环达到while()的目的,但本质上还是while循环,争辩具体的for()还是while()没有价值。
第二,list可以直接按值删除全部等于该值的所有节点,但是有的时候这个函数确不好用,比如我们要删除一堆点集(xi,yi,zi)中,凡是x,y相等的点只留一个,对z没有要求。这时候用按值删除就很困难的,因为x,y相等但z不一定相等。
所以单次遍历删除所有满足条件的节点,这还是很实用的,也是可以做到非常的灵活的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值