【STL】删除元素 remove、erase

11 篇文章 0 订阅

【STL】删除元素 remove、erase

STL功能很强大,但是说到删除元素,不少人会觉得有些上头。

删除元素,第一印象可能就是eraseremove

remove

remove,在STL不止一个,有全局std::remove函数,list容器中,还有一个名为remove的成员函数;两个函数的名称完全一样,很多人傻傻分不清。

有以下几点区别:

  1. std::remove适用于很多容器,list只是其中之一;而list的成员函数remove,既然是成员函数,肯定是它独享咯!

  2. std::remove其实并没有真正从容器中删除元素;list的成员函数remove,将list中满足条件的元素真正删除了!

这是最重要的两点,其他类似参数不一样、返回值不一样等等,就不赘述了!上代码:

template<typename T>
void PRINT_ALL(const T& rColl, const char* pRemind)
{
    std::cout << pRemind;
    for (const auto& ele : rColl)
        std::cout << ele << " ";
    std::cout << std::endl;
}
template<typename T>
void InitData(T& rValue)
{
    rValue.clear();
    rValue = { 1, 5, 4, 4, 7, 4, 2 };
}
int _tmain(int argc, _TCHAR* argv[])
{
    using std::cout;
    using std::endl;
 
    std::list<int> lstInt;
 
    InitData(lstInt);
    PRINT_ALL(lstInt, "原始数据:");
    std::remove(lstInt.begin(), lstInt.end(), 4);
    PRINT_ALL(lstInt, "std::remove后:");
 
    InitData(lstInt);
    PRINT_ALL(lstInt, "原始数据:");
    lstInt.remove(4);
    PRINT_ALL(lstInt, "调用成员函数remove后:");
 
    system("pause");
    return 0;
}

这里,因为经常要打印,将打印函数封装了一下,还封装了一个初始化数据的函数,执行结果如下:

显而易见,std::remove并没有删除容器中的元素,但似乎又有些变化。list的成员函数remove,成功删除元素!

所以,对list来说,直接用其成员函数remove即可!除了remove外,list还有remove_if成员函数,对于简单删除元素来说,基本满足绝大部分要求。

但是!!!vector呢?它没有remove的成员函数,怎么办?

只能从std::remove来想办法啦!来看看std::remove函数的原型:
在这里插入图片描述

有一个返回值!!!啥意思呢?
在这里插入图片描述

说人话就是:返回的是没有被删除的最后一个元素的位置,从first到这个返回值,就是不等于val的所有元素的序列

请注意上面标红的那一句,first表示的是容器迭代器的起始位置,换句话说,只要保留从first到返回值这个区间的元素,就能达到我们的目的。再转换一下思维,如果删除了从这个返回值到end的元素,是不是就可以了?

如何删除一个区间呢?需要另一个函数,erase!

erase

不管是list容器,还是vector容器,都有成员函数erase,原型如下:
在这里插入图片描述
在这里插入图片描述

可以说长得一样,都有两种形式。

接收一个迭代器作为参数,表示删除指定迭代器位置的元素;以[first,last)为区间的两个迭代器,删除这个区间内的所有元素。

单参数的暂时先不管,双参数的似乎可以满足上面remove的要求,删除std::remove返回值到end这个区间的所有元素,试试看:
在这里插入图片描述
所有其他代码都不变,就修改了划线的那一句,执行结果如下:
在这里插入图片描述

果然可以满足需求!

erase还有一个单参数的形式,这个也是很多公司面试时经常考的一个点!

看如下代码:

int _tmain(int argc, _TCHAR* argv[])
{
    using std::cout;
    using std::endl;
 
    std::list<int> lstInt;
 
    InitData(lstInt);
    PRINT_ALL(lstInt, "原始数据:");
    
    std::list<int>::iterator iter = lstInt.begin();
    for (; iter != lstInt.end(); ++iter)
    {
        if (4 == *iter)
            lstInt.erase(iter);
    }
    PRINT_ALL(lstInt, "erase后:");
 
 
    system("pause");
    return 0;
}

这里,没有调用std::remove,直接遍历lstInt中的所有元素,删除值为4的元素。看起来好像很好,可是一运行,程序直接崩溃!

在这里插入图片描述

为何?原因就出在这个erase上。

执行完lstInt.erase(iter);后,iter这个迭代器就失灵了,在对其做++iter操作,肯定会出现问题。

怎么办?看erase函数有个返回值,其解释如下:

在这里插入图片描述

啥意思?最后一个被删除的元素的下一个元素。

OK!问题简单了,lstInt.erase(iter);后iter失灵,但是返回值是被删除的元素的下一个,那把返回值赋值给iter,不就行了?

修改程序,如下:
在这里插入图片描述

执行后,结果如下:
在这里插入图片描述

不崩溃,但结果也不对,还有一个4没删除掉!

为何如此?

原始数据为 1 5 4 4 7 4 2

当遇到第一个4时,iter = lstInt.erase(iter);,第一个4被删除,iter赋值后指向第二个4,重新执行循环语句,会++iter,iter的指向又变了,到了第二个4之后也就是7的位置。这第二个4,就成了漏网之鱼。

如何解决?

分析一下,当iter指向的元素等于指定元素时,执行iter = lstInt.erase(iter)后,其实,已经做了一次后移操作,iter指向的就是被删除元素的后一个,所以,这时候不应该再++iter,只有当iter指向的元素不等于指定元素时才后移。

很容易看出来,这就是一个if…else的条件判断,修改代码如下:
在这里插入图片描述

这里,做了两处修改,一处是从for循环里把++iter去掉了;另一处,是加了一句else ++iter。

执行结果如下:

在这里插入图片描述

成功!!

总结一下:

1、如果是list容器,直接调用成员函数remove/remove_if删除元素;

2、调用std::remove,想要删除元素,必须配合erase成员函数才行,将std::remove返回的迭代器至end全部删除;

3、如果遍历元素删除,要小心erase函数,删除后的迭代器不可用。
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值