STL算法移动范围:简单、高效的集合移动方法

news2024/11/29 8:55:28

STL算法移动范围

  • 一、简介
  • 二、批量移动集合中的多个元素
    • 2.1、std::copy
    • 2.2、std::move
    • 2.3、std::swap_ranges
  • 三、在一个范围内变换子范围
    • 3.1、std::copy_backward 向前复制
    • 3.2、元素倒退
    • 3.3、交换子范围
  • 四、这一切太复杂了
  • 五、总结

一、简介

已经介绍过使用STL算法在范围(range)上实现复杂操作的各种方法。本文将重点介绍如何在STL中移动集合中的元素,以及一些常用的移动算法的实现和用法。包括批量移动元素、子范围变换和交换元素等。

移动集合中的元素在实际应用中非常常见,对于提高程序效率和减少资源占用都具有重要意义。通过合理选择移动算法,可以更加高效地操作集合中的数据,实现更灵活的功能。

二、批量移动集合中的多个元素

基本上有三种允许批量移动集合中的多个元素的STL算法:std::copystd::movestd::swap_ranges

2.1、std::copy

std::copy可能是STL列表中最简单的算法。它接受一个输入范围(以两个迭代器的形式,和现在的STL接口一样)和一个输出迭代器:

template<typename InputIterator, typename OutputIterator >
OutputIterator copy(InputIterator first, InputIterator last, OutputIterator out);

它只是简单地将输入范围的每个元素复制到输出迭代器中,每一步增加一个元素。
在这里插入图片描述
当它的一个输入或输出没有绑定到容器时,它会变得更加微妙。例如,将输出迭代器绑定到流的情况:

std::vector<int> v = {1, 2, 3, 4, 5};

std::copy(begin(v), end(v), std::ostream_iterator<int>(std::cout));

输出:

12345

std::copy的另一个微妙之处是,如果范围的元素类型的复制构造函数满足某些条件(更准确地说,如果它是std::is_trivially_copyable), std::copy可以调用std::memmove来批量获取内存块,而不是对每个元素调用复制构造函数。

但总的来说,这不是一个非常微妙的算法。

注意,std::copy有一个对应的"_n ":std::copy_n。它以begin迭代器和大小的形式接受输入范围,而不是beginend

template<typename InputIterator, typename Size, typename OutputIterator>
OutputIterator copy_n(InputIterator first, Size count, OutputIterator out);

此外,要将范围复制到STL容器中,不仅仅只有std::move请,还有其他方法可以有效地将多个元素插入STL容器中,会有专门的文章介绍。

2.2、std::move

std::move是C++ 11带来的最基本的标准函数之一,如果还不知道,那你需要好好的重温以下C++ 11的知识了。

如果了解std::move,是否也知道std::move对范围也有重载呢?和std::copy一样,它接受两个输入迭代器和一个输出迭代器:

template<typename InputIterator, typename OutputIterator>
OutputIterator move(InputIterator first, InputIterator last, OutputIterator out);

它将输入范围的每个元素都移动到输出迭代器中:
在这里插入图片描述
这是一种不同于将迭代器移动来让STL移动元素的方法。

2.3、std::swap_ranges

顾名思义,std::swap_ranges将第一个范围中的每个元素与第二个范围中的对应元素交换:
在这里插入图片描述
注意,这两个范围不允许重叠。
有点奇怪的是,std::swap_rangestd::move 的名称不对称,也许使用 std::move_ranges 或者对 std::swap 重载会更一致。算了,尊重标准吧。

还要注意std::swap_ranges是一个“1.5 range”,也就是说它不接受第二个范围的末尾:

template<typename ForwardIterator1, typename ForwardIterator2>
ForwardIterator2 swap_ranges(ForwardIterator1 first1, ForwardIterator1 last1,
                             ForwardIterator2 first2);

它假设第二个范围至少和第一个范围一样大,所以在调用std::swap_ranges之前,需要确保这个假设是正确的。

三、在一个范围内变换子范围

上述三种算法可以将数据从一个范围传输到另一个范围。但如果这两个范围实际上是更大范围的两个子范围呢?如果这些子范围重叠呢?

3.1、std::copy_backward 向前复制

假如想要复制一个范围的子部分到该范围某个的位置,这个新位置可能位于第一个子范围的末尾之前。

例如,一个1到10的范围:
在这里插入图片描述

想将1到5的子范围向后移动3个位置:
在这里插入图片描述
第一反应可能是使用std::copy

std::copy(begin(v), begin(v) + 5, begin(v) + 3);

或者,std::copy_n

std::copy_n(begin(v), 5, begin(v) + 3);

但是,这不是这个操作的正确算法,原因是:

  • 第一个原因是,它不会得到我们期望的结果。就像下面这样:
    在这里插入图片描述
    哦~~,第一步就失去了4的值。
  • 第二个原因是标准要求输出迭代器不在[begin, end]之内。因此,std::copy实际上具有未定义行为。这有一个奇怪的含义,即禁止在自身上复制一个范围。

因此,要在一个范围内向前复制值,需要一个与std::copy相同的算法,但是向后(这听起来有点奇怪)。

这就是为什么有…std::copy_backwardstd::copy_backward类似于std::copy,不同之处在于它首先将输入范围的最后一个元素复制到输出范围的最后一个元素:
在这里插入图片描述
然后它从这里开始工作,一直到输入范围的开始:
在这里插入图片描述
这意味着指向输出范围的输出迭代器必须是它的end

template<typename BidirectionalIterator1, typename BidirectionalIterator2>
BidirectionalIterator2 copy_backward(BidirectionalIterator1 first, BidirectionalIterator1 last, 
									 BidirectionalIterator2 outLast);

所以,例子中的代码应该这样:

std::copy_backward(begin(v), begin(v) + 5, begin(v) + 8);

注意,还有std::move_backward,它移动一个范围的元素,从它的末尾开始,一直移动到它的开始。

3.2、元素倒退

与上面类似的推理,要向后移动,可以使用std::copy(或std::move)。实际上,如果std::copy_backward的输出迭代器位于输入范围的(begin, end)内,则是未定义的行为。

3.3、交换子范围

可以使用std::swap_ranges在一个范围内交换两个子范围,只要它们不重叠即可。

四、这一切太复杂了

使用std::copy_backward向前移动元素,确保所有的开始和结束迭代器都是正确的,以避免超出范围……这一切看起来都很复杂。

因此,有学者提议在C++ 20标准中增加一个std::shift_left和一个std::shift_right函数。建议的函数原型为:

template<typename ForwardIterator>
ForwardIterator shift_left(ForwardIterator first, ForwardIterator last,
                           typename std::iterator_traits<ForwardIterator>::difference_type n);

template<class ForwardIterator>
ForwardIterator shift_right(ForwardIterator first, ForwardIterator last,
                            typename std::iterator_traits<ForwardIterator>::difference_type n);

最后一个参数表示移动元素的步数,因此:

std::shift_right(begin(v), begin(v) + 5, 3);

会把范围3的前5个元素的位置移到范围下面。注意:这两个函数会移动,而不会复制元素。然而,C++ 20并没有对应的函数,但是有人已经实现了该函数,并上传在github上。

五、总结

本文详细介绍了在STL中批量移动集合中的多个元素的几种常见算法,包括std::copystd::movestd::swap_ranges。通过这些算法,我们可以在不同的情况下灵活地对集合进行移动操作,实现数据的复制、移动和交换。

另外,还介绍了在一个范围内变换子范围的方法,以及如何向前复制元素、元素倒退和交换子范围。这些技巧可以帮助我们更好地处理集合中元素的移动和交换操作,提高代码的效率和可读性。

最后,虽然C++ 20标准没有引入std::shift_leftstd::shift_right函数,但可以通过自定义实现或使用第三方库来实现这些功能,以满足特定需求和提高编程效率。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1658937.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

微信小程序miniprogram_npm目录主包太大如何处理

在使用TDesign开发小程序&#xff0c;miniprogram_npm目录下很多组件&#xff0c;感觉很多组件我都没引用&#xff0c;为什么都在主包来&#xff0c;导致主包很大。如何优化&#xff1f; 在使用npm构建后生成的miniprogram_npm目录里&#xff0c;有所有TDesign组件。但是有很…

视频剪辑批量转码技巧:如何将MP4视频快速转换为MP3音频的方法

在视频剪辑和音频处理的领域中&#xff0c;经常需要将视频文件转换为音频文件&#xff0c;特别是将MP4视频转换为MP3音频。这样的转换不仅可以减少文件大小&#xff0c;方便传输和存储&#xff0c;还可以在不损失音频质量的情况下&#xff0c;方便在各种设备上播放。下面&#…

【AI基础】第一步:安装python开发环境-conda篇

上一篇讲解了朴素的开发环境安装 - 【AI基础】第一步&#xff1a;安装python开发环境-windows篇-CSDN博客&#xff0c;这里讲解通过conda来配置python环境&#xff0c;同时安装python开发工具pycharm&#xff0c;最终输出python版本的helloworld。 一、安装miniconda 1.1 下载…

计算机系列之结构化开发方法

23、结构化开发方法 1、系统分析与设计概述 ◆系统分析过程一般按如图所示的逻辑进行&#xff1a; (1)认识、理解当前的现实环境&#xff0c;获得当前系统的“物理模型“ (2)从当前系统的“物理模型“抽象出当前系统的“逻辑模型“ (3)对当前系统的“逻辑模型”进行分析和…

Pycharm2024版,更换安装源

1、选择Python Packages 2、点击图中的小齿轮 3、点击 号 4、添加源地址 常用源如下&#xff1a; 清华&#xff1a;https://pypi.tuna.tsinghua.edu.cn/simple 阿里云&#xff1a;http://mirrors.aliyun.com/pypi/simple/ 中国科技大学 https://pypi.mirrors.ustc.edu.cn…

测试docker GPU性能损失

NVIDIA 3090 利用HSOpticalFlow代码测试docker GPU性能损失 docker介绍图如下&#xff1a; 形象生动展示了他们之间的关系 今天要测试docker容器运行HSOpticalFlow算法的性能损失&#xff0c;包括CPU和GPU 上一篇博客 http://t.csdnimg.cn/YW5kE 我已经介绍了使用docker和nvid…

Visual Studio编译QT工程

1、安装QT 2、安装VS 3、选择扩展和更新 4、搜索Qt Visual Studio Tools&#xff0c;安装或卸载 5、安装成功后工具栏显示Qt VS Tools 6、配置Qt VS Tools&#xff1a;打开Qt VS Tools的下拉菜单&#xff0c;选择Qt Versions 7、选择qt qmake.exe 的路径

html--第一个网页

<!doctype html> <html> <head> <meta charset"utf-8"> <title>CSS霓虹灯动画</title><style> import url("https://fonts.googleapis.com/css?familyPoppins:900&displayswap"); body {background-color:…

Docker Desktop 修改容器的自启动设置

Docker Desktop 允许用户控制容器的自启动行为。如果你不希望某个容器在 Docker 启动时自动启动&#xff0c;你可以通过以下步骤来更改设置&#xff1a; 1. 打开 Docker Desktop 应用。 2. 点击右上角的设置&#xff08;Settings&#xff09;按钮&#xff0c;或者使用快捷键 Cm…

民航电子数据库:在console或服务器登录数据库

目录 前言登录切换数据库 前言 在不使用数据库管理工具的情况下&#xff0c;可以在console或服务器上操作数据库&#xff0c;这时就需要使用相关命令登录到数据库 登录 caeconsole nssl IP地址 端口 数据库名称 用户名 密码 切换数据库 use 数据库名称

【网络安全】一次sql注入问题的处理

目录 问题 10.60.100.194&#xff0c;修改之前 修改方案 问题解决 测试过程 问题思考与总结 问题 一次sql注入问题的筛查报告&#xff0c;主要是sql注入的问题资源-CSDN文库 doc-new\20-设计文档\34-Mesh设备管理\100-网络安全 10.60.100.194&#xff0c;修改之前 修改…

Redis-新数据类型-Geospatia

新数据类型-Geospatia 简介 GEO&#xff0c;Geographic,地理信息的缩写。 该类型就是元素的二维坐标&#xff0c;在地图上就是经纬度。Redis基于该类型&#xff0c;提供了经纬度设置、查询、范围 查询、距离查询、经纬度Hash等常见操作。 常用命令 geoadd key longitude lat…

全彩屏负氧离子监测站

TH-FZ5随着科技的飞速发展和人们对生活品质追求的日益提高&#xff0c;空气质量已经成为人们日常生活中不可忽视的重要话题。为了更直观地了解身边的空气质量&#xff0c;一款全新的全彩屏负氧离子监测站应运而生&#xff0c;它不仅能够实时监测负氧离子含量&#xff0c;还能同…

达到新高度:SpaceX 的北极星黎明任务为商业太空行走铺平道路

概述 在私人太空探索时代&#xff0c;SpaceX 凭借其最新任务“北极星黎明”正在突破载人航天的界限。该任务计划于今年夏天发射&#xff0c;标志着该公司雄心勃勃的计划中的一个重要里程碑&#xff0c;该计划旨在使人类成为多行星物种。在本文中&#xff0c;我们将深入探讨北极…

vue+element的表格(el-table)排班情况表(2024-05-09)

vueelement的表格&#xff08;el-table&#xff09;排班情况&#xff0c;增删查改等简单功能 代码&#xff1a; <template><!-- 表格 --><div class"sedules"><el-header><el-date-pickerv-model"monthValue2"type"month…

【复试分数线】C9历年分数线汇总(第二弹)

今天我将分析C9中主要考信号的5所院校&#xff1a;复旦大学、上海交通大学、南京大学、哈尔滨工业大学、西安交通大学。 这次会为大家整理四电四邮的整理了近三年各院校的复试分数线作为参考&#xff0c;大家可以参考&#xff01; 大多数院校采取的是1.2:1差额的形式复试。举…

[Linux][网络][网络层][IP协议]详细讲解

目录 0.基本概念1.IP协议头格式2.IP分片与组装1.为什么要分片&#xff1f;2.分片后谁来组装&#xff1f;3.这个分片操作传输层知道吗&#xff1f;4.如何识别报文和报文的不同&#xff1f;5.接收端&#xff0c;如何得知报文是独立的还是一个分片&#xff1f;6.如何区别哪些分片是…

MyBatis(该篇足已)

目录 一.MyBatis是什么&#xff1f; 二.为什么学习MyBatis呢&#xff1f; 三.MyBatis的学习 3.1MyBatis的开发流程 3.2MyBatis项目 四.MyBatis的增删改操作 五.参数占位符 #{} 和 ${} 六.映射返回 七.映射失败 八.数据库连接池 九.动态SQL 9.1<if>标签 9.2&…

JavaSE——异常(2/2)-异常的处理(记录异常并提示 、尝试重新修复)

目录 记录异常并提示 案例演示 流程解析 写法优化 尝试重新修复 开发中对于异常的常见处理方式 一层一层往上抛出异常&#xff0c;并且在最上层捕获异常&#xff0c;分为两种不同的处理方式。 例如&#xff0c;B站网页报错就是采取的第一种方式&#xff1a; 记录异常并…