当前位置: 首页 > news >正文

stl之remove、erase算法与元素的删除

Effective Stl第32条中强调,remove不是真正意义上的删除,因为它做不到。我们先看一下remove的声明:

template<class ForwardIterator,class T>
ForwardIterator remove(ForwardIterator first, ForwardIterator last, const T&value);

remove的参数是一对迭代器,指定所要操作的元素区间。但是它并不接受容器作为参数,所以remove并不知道这些元素存放在哪个容器中。并且,remove也不可能推断出容器类型。

所以调用remove并不会使容器元素数量减少。平时我们删除容器元素,首先想到的就是erase方法,但是erase方式是容器的成员方法,这也是stl中除list外删除容器元素的唯一方法。

#include "pch.h"
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main() {
	vector<int> v;
	v.reserve(10);                     /*创建一个容器并初始化大小为10,填入1-10.关于reverse的解释请看我的另一篇博客-stl用法之reverse*/
	for (int i = 1; i <= 10; ++i) {
		v.push_back(i);
	}
	cout << v.size() << endl;          //输出大小,10
	v[3] = v[5] = v[9] = 99;
	remove(v.begin(), v.end(), 99);    //调用remove
	cout << v.size() << endl;          //输出大小,仍然是10
}

运行结果:

以上代码验证了前面所说的,remove并不会删除容器元素。那remove到底做了什么呢?我们先打印一下调用remove之后容器里的元素

简而言之:remove移动了区间中的元素,其结果是,“不用 被删除”的元素移动到了区间的前部,并且保持原来的相对顺序。它返回的迭代器是指向最后一个“不用被删除”的元素之后的元素。这个返回值相当于该区间“新的逻辑结尾”。

就上边的例子,调用remove之前v的布局如下:

调用remove之后,v的布局如下

结论:“不用被删除”的元素在v.begin()和newEnd之间,“需要被删除”的元素在newEnd和v.end()之间。

实际上,对于向量v,remove做了如下的操作:

1.remove检查v[0],看它的值是否需要被删除,接着看v[1],然后是v[2],依次遍历检查

2.检查到v[3],发现它应该被删除,于是它记住v[3]的值要被覆盖,然后移动到v[4]上。这就好比注明了v[3]是一个需要被填充的洞。

3.检查v[4],发现它的值需要保留,所以把v[4]赋给v[3],并记住v[4]需要覆盖。移动到v[5]。

4.移动到v[5],v[5]需要被删除,记录,移动到v[6],此时它记录v[4]、v[5]都是需要被填充。v[6]需要被保留,v[6]赋值给v[4]。

5.用类似的方式检查v[7]v[8]v[9],它吧v7赋给v5,v8赋给v6,忽略v9,因为v9需要被删除。

6.它返回一个迭代器,指向下一个要被覆盖的元素,该例中为v7。

 

通过以上分析,我们可以得出结论:remove不会删除元素,它只是在移动元素。所以,如果想删除元素,那么必须在remove之后调用erase。

上面例子中,如果我们想删除值为99的元素,可以这样写:

v.erase(remove(v.begin(), v.end(), 99),v.end());

最后,list的成员函数remove也是使用的这种方法删除元素,这是stl中唯一一个名为remove并且确实删除了容器中元素的函数。

#include "pch.h"
#include <iostream>
#include <algorithm>
#include <list>
#include <vector>
using namespace std;
int main() {
	list<int> ll;        /*创建一个容器并初始化大小为10,填入1-10.关于reverse的解释请看我的另一篇博客-stl用法之reverse*/
	for (int i = 1; i <= 10; ++i) {
		ll.push_back(i);
	}
	cout << ll.size() << endl;          //输出大小,10
	ll.remove(9);
	cout << ll.size() << endl;
}

相关文章:

  • docker学习之旅二:docker基本命令
  • docker学习之旅一:ubuntu下安装docker+配置阿里源
  • redis-跳跃表zskiplist
  • redis集群介绍与搭建
  • Linux系统命令与网络、磁盘参数和日志监控命令
  • mysql 8.0版本修改密码
  • 解决Navicat 连接mysql报错:Can‘t connect to MYSQL server on “ip address“(10061)
  • jsoncons使用之: 构造json
  • 使用reserve来避免不必要的内存重新分配
  • redis 编译报错 zmalloc.h:50:10: fatal error: jemalloc/jemalloc.h: 没有那个文件或目录
  • linux下hiredis安装、C接口编程
  • redis源码学习之数据结构---双向链表
  • redis源码分析--事件驱动模型
  • ubuntu下zmq编译安装及请求-应答模式测试
  • c++输出:怎么解决数字过大时默认使用科学计数法输出的问题?
  • CODING 缺陷管理功能正式开始公测
  • ES6, React, Redux, Webpack写的一个爬 GitHub 的网页
  • ES6简单总结(搭配简单的讲解和小案例)
  • Java应用性能调优
  • js对象的深浅拷贝
  • JS进阶 - JS 、JS-Web-API与DOM、BOM
  • linux学习笔记
  • tweak 支持第三方库
  • vue2.0开发聊天程序(四) 完整体验一次Vue开发(下)
  • Webpack 4x 之路 ( 四 )
  • 从输入URL到页面加载发生了什么
  • 融云开发漫谈:你是否了解Go语言并发编程的第一要义?
  • 如何使用 JavaScript 解析 URL
  • 入手阿里云新服务器的部署NODE
  • 少走弯路,给Java 1~5 年程序员的建议
  • ionic入门之数据绑定显示-1
  • postgresql行列转换函数
  • 扩展资源服务器解决oauth2 性能瓶颈
  • 昨天1024程序员节,我故意写了个死循环~
  • # 飞书APP集成平台-数字化落地
  • #Spring-boot高级
  • (HAL)STM32F103C6T8——软件模拟I2C驱动0.96寸OLED屏幕
  • (Redis使用系列) SpringBoot中Redis的RedisConfig 二
  • (ZT) 理解系统底层的概念是多么重要(by趋势科技邹飞)
  • (动手学习深度学习)第13章 计算机视觉---图像增广与微调
  • (详细版)Vary: Scaling up the Vision Vocabulary for Large Vision-Language Models
  • (转)nsfocus-绿盟科技笔试题目
  • .gitignore文件设置了忽略但不生效
  • .NET CORE 2.0发布后没有 VIEWS视图页面文件
  • .NET NPOI导出Excel详解
  • .Net下使用 Geb.Video.FFMPEG 操作视频文件
  • .NET与 java通用的3DES加密解密方法
  • .net与java建立WebService再互相调用
  • /*在DataTable中更新、删除数据*/
  • :中兴通讯为何成功
  • @entity 不限字节长度的类型_一文读懂Redis常见对象类型的底层数据结构
  • [.net] 如何在mail的加入正文显示图片
  • [.net]官方水晶报表的使用以演示下载
  • [2016.7.Test1] T1 三进制异或
  • [20160807][系统设计的三次迭代]