C++高阶-STL之容器元素的复制和变换

copy()算法

copy()算法可以接受三个参数,前两个参数表示需要复制的源容器的起始位置和终止位置,它们共同定义了需要复制的数据元素的范围。第三个参数则是目标容器的起始位置

copy()算法将前两个参数所表示的源容器范围内的数据,复制到第三个参数表示的目标容器的开始位置并依次往后。复制完成后, copy()算法会返回指向目标容器中被复制元素的结束位置,也就是最后一个元素的下一个位置。

要保证在目标容器的复制开始位置之后有足够的空间来容纳即将被复制进来的数据。必要的时候,可以根据被复制数据的多少,使用 vector 容器的 resize()函数调整目标容器的大小

当复制行为发生在同一个容器中时,复制数据的源范围最好不要和目标范围相互重叠,否则很可能造成数据的混乱。

// 保存 C1 和 C2 班级成绩的容器
vector<int> vecScoreC1;
vector<int> vecScoreC2;
// 对容器进行操作,将各个班级的成绩保存到各自的容器中
// …
// 保存所有成绩的成绩总表容器
vector<int> vecScore;
// 根据各个分容器的大小,重新设定总容器的容量,
// 使它可以容纳即将复制进来的所有数据
vecScore.resize( vecScoreC1.size() + vecScoreC2.size() ) ;
// 将第一个容器 vecScoreC1 中的数据复制到 vecScore 中
auto lastit = copy(vecScoreC1.begin(),
vecScoreC1.end(), // 复制的范围
vecScore.begin() ); // 复制的目标位置
// 将第二个容器 vecScoreC2 中的数据追加到 vecScore 的末尾
copy(vecScoreC2.begin(),
vecScoreC2.end(),
lastit ); // 以上一次复制的结束位置作为第二次复制的开始位置

copy_backward()算法

使用 copy()算法复制数据,我们需要指定被复制数据在目标容器中的开始位置,然而有的时候,这个开始位置很难确定,而相应的结束位置却很好确定,或者是我们对结束位置有特殊的要求,在这种情况下,我们就可以使用 copy()算法的一个变种——copy_backward()算法。

它跟 copy()算法的使用非常相似,两者唯一的不同就是第三个参数和返回值的意义。首先是算法的第三个参数,在 copy()算法中,它是被复制数据在目标容器中的开始位置,而在 copy_backward()算法中,它成了结束位置。也就是说, copy_backward()算法会保证被复制的数据在这里结束。其次是返回值, copy()算法返回的是指向被复制元素在目标容器中的结束位置的迭代器,而 copy_backward()算法返回的迭代器指向的却是这些元素的开始位置

// 保存学生对象的容器
vector<Student> vecStudent;
// 将数据保存到容器中…
// 扩大容器的容量为原来的两倍
// 这样容器中前半部分是已有的数据,后半部分是默认生成的数据
vecStudent.resize( vecStudent.size() * 2 );
// 将前半部分已有的数据复制到后半部分,替换掉后半部分默认的数据
copy_backward(vecStudent.begin(),
vecStudent.begin() + vecStudent.size() / 2, // 前半部分范围
vecStudent.end() ); // 指定结束位置,复制数据填满容器

copy_if()算法

copy_if()算法需要指定源容器中被复制数据的范围以及目标容器中的起点位置,还可以接受一个函数作为第四个参数,用以表达我们对数据的筛选规则。这个函数的返回值为 bool 类型,拥有一个跟被复制数据类型相同的参数。

在进行复制的时候, copy_if()算法会逐个将复制范围内的数据传递给这个函数进行判断,只有符合筛选规则,也就是规则函数返回值为 true 的数据才会被复制。这样,我们就可以将源容器中的符合筛选条件的数据复制到目标容器中,实现数据的筛选

// 引入需要的头文件
#include <vector>
#include <algorithm> // 为了使用 copy_if()算法
#include <iostream>
using namespace std;
// 复制规则函数
bool isgood(int n)
{
	// 优秀的成绩(大于等于 85)才会返回 true,才会被复制
	return n >= 85 ? true : false;
}
int main()
{
	// 保存所有成绩的源容器
	vector<int> vecAll = {64,89,91,68,99,75};
	// 用于保存筛选出来的优秀学生成绩的目标容器
	vector<int> vecGood;
	// 为目标容器预留足够的空间
	vecGood.resize(vecAll.size());
	// 使用 copy_if()算法将源容器中的优秀成绩复制到目标容器中
	copy_if(vecAll.begin(),vecAll.end(), // 复制的范围
	vecEnroll.begin(), // 目标容器的起始位置
	isgood); // 复制规则
	// 输出筛选出来的优秀成绩
	cout<<"筛选出来的优秀成绩: "<<endl;
	for(int n : vecGood)
	{
		if(0 != n) // 过滤掉目标容器中未被覆盖的默认成绩
		cout<<n<<endl;
	}
	return 0;
}

合并容器算法-merge()

merge()算法就是将两个源容器中的数据合并( merge)复制到目标容器中,以此实现数据的“汇总”。

template <class InputIterator1, class InputIterator2, class OutputIterator>
OutputIterator merge (InputIterator1 first1, InputIterator1 last1,
					  InputIterator2 first2, InputIterator2 last2,
                      OutputIterator result );

( first1,last1)和( first2,last2)这两个迭代器对分别表示两个源容器中被复制数据范围的起始位置和结束位置,而 result 则表示合并到目标容器的起始位置

merge()算法要求源容器中的数据必须是经过排序的

// 使用 sort()算法对两个源容器中的数据排序
sort(vecScoreC1.begin(), vecScoreC1.end());
sort(vecScoreC2.begin(), vecScoreC2.end());
// 调整目标容器的大小,以便容纳合并进来的数据
vecScore.resize(vecScoreC1.size() + vecScoreC2.size());
// 使用 merge()算法将 vecScoreC1 和 vecScoreC2 两个容器中的数据
// 合并到目标容器 vecScore 中
merge(vecScoreC1.begin(), vecScoreC1.end(), // 第一个容器的范围
vecScoreC2.begin(), vecScoreC2.end(), // 第二个容器的范围
vecScore.begin()); // 目标容器的起始位置

set_union()

参与合并的两个源容器中往往有一些相同的数据,而这些相同的数据都会出现在合并后的容器中。也就是说,相同的数据在目标容器中出现了两次,这样就造成了数据的冗余。

为了实现这种删除冗余数据的合并, STL 提供了 set_union()算法。 set_union 算法的使用跟merge()算法完全相同,但它在合并两个源容器中的数据时,如果遇到两个源容器中都有的相同数据,它不会像 merge()算法一样将两份相同数据都合并到目标容器中,而只合并一份数据到目标容器中,这样目标容器中就不会有相同的冗余元素了。

// 定义总商品清单
vector<string> vecGoods;
// 文具类货物清单
vector<string> vecStationaries = {"Pen","Erase","Pencil"};
// 办公用品类商品清单
vector<string> vecOfficeSupplies = {"Folder","Pen","Notepad"};
// 根据源容器的数据多少调整目标容器的大小
vecGoods.resize( vecStationaries.size() + vecOfficeSupplies.size() );
// 使用 sort()算法对源容器进行排序
sort(vecStationaries.begin(), vecStationaries.end());
sort(vecOfficeSupplies.begin(), vecOfficeSupplies.end());
// 使用 set_union()算法将源容器中的数据合并到目标容器中
// set_union()算法返回的迭代器指向的是合并后的有效数据的结束位置
auto itend = set_union(
vecStationaries.begin(),
vecStationaries.end(),// 第一个容器的范围
vecOfficeSupplies.begin(),
vecOfficeSupplies.end(), // 第二个容器的范围
vecGoods.begin()); // 目标容器的起始位置
// 输出合并后的商品
for(auto it = vecGoods.begin();
it != itend; // 以 set_union()算法返回的迭代器作为结束位置
++it)
{
	cout<<*it<<endl;
}

从集合的角度来看,

  • set_union()算法实际上计算得到的是两个容器的并集。
  • set_difference()算法用于计算两个容器的差集。
  • set_intersection()算法则可以用于计算两个容器的交集。

变换容器元素: transform 函数

template < class InputIterator, class OutputIterator, class UnaryOperator >
	OutputIterator transform ( InputIterator first1, InputIterator last1,
							   OutputIterator result, UnaryOperator op );
template < class InputIterator1, class InputIterator2,
		  class OutputIterator, class BinaryOperator >
	OutputIterator transform ( InputIterator1 first1, InputIterator1 last1,
							   InputIterator2 first2, OutputIterator result,
							   BinaryOperator binary_op );

transform()算法有两个版本,其中,第一个版本的 transform()算法的使用跟 copy()算法的使用相似,它可以接受 4 个参数,前两个参数用于指定源容器中数据的起始位置和结束位置;第三个参数用于指定目标容器的起始位置;最后一个参数就是要在移动过程中执行的动作,通常我们用一个函数(包括函数对象和 Lambda 表达式)表示。因为这个函数的返回值会被加入到目标容器,所以其返回值类型跟目标容器所保存数据的类型相同;同时,在移动数据的过程中,因为它会被以源容器中的数据作为参数进行调用,所以它拥有一个参数,其类型跟源容器所保存数据的类型相同。

例如,在考试结束后,老师需要根据学生的考试成绩为学生评定等级,这时,就需要 transform()算法将具体的考试成绩变换为等级:

// 将考试成绩转换为学生等级的转换函数
char RateScore(int nScore)
{
	if(nScore >= 85) // 成绩高于等于 85 的是 A 等
	{
		return 'A';
	}
	else if(nScore >= 60)// 成绩高于等于 60 的是 B 等
	{
		return 'B';
	}
	else // 成绩低于 60 的就是 C 等
	{
		return 'C';
	}
}
//…
// 保存原始的考试成绩的源容器
vector<int> vecScore = {89,72,94,63,58};
// 保存学生等级的目标容器
vector<char> vecGrade;
// 根据源容器的数据多少调整目标容器的容量
vecGrade.resize(vecScore.size());
// 使用 transform()算法将源容器中的学生成绩转换为学生等级并保存到目标容器
// RateScore()函数负责具体的转换
transform(vecScore.begin(),
vecScore.end(), // 参与转换的源容器范围
vecGrade.begin(), // 目标容器的起始位置
RateScore); // 转换函数
// 输出转换后的学生等级
for(char cGrade : vecGrade)
{
	cout<<cGrade<<endl;
}

第二个版本的 transform()算法的使用跟第一个版本基本相似,只是因为它要同时将两个容器的数据作为输入,所以它在第一个版本的基础上增加了一个参数 first2,用来表示第二个源容器输入数据的起始位置。同时,因为它的处理函数需要处理两个源容器中的数据,所以它需要两个参数,分别用来接收来自两个源容器中的数据,其类型自然也就分别与两个源容器所保存数据的类型相同了。除此之外,其他参数的意义跟第一个版本的 transform()算法相同。

例如,学生的数学成绩和英语成绩分别保存在两个容器中,现在班主任要将两个成绩总和起来,看看学生的综合成绩:

// 定义移动数据过程中的操作函数
// 因为要接收来自两个源容器的数据,所以它有两个参数
// 这里,对数据的处理只是将来自两个容器中的数据简单相加取和值 
int add( int nScoreMath, int nScoreEng )
{
	return nScoreMath + nScoreEng;
}
// 保存学生分科成绩的容器
// 这里,因为两个容器中的数据要配合操作,
// 所以应该保证两个容器中的数据多少一致
vector<int> vecScoreMath = {98,85,96,75,68};
vector<int> vecScoreEng = {68,82,93,62,88};
// 定义保存综合成绩的容器
vector<int> vecScore;
// 改变容器的容量,让它有足够的空间保存结果
vecScore.resize( vecScoreMath.size() );
// 将 vecScoreMath 和 vecScoreEng 容器中的成绩相加,
// 将结果保存到结果容器 vecScore 中
transform(vecScoreMath.begin(), vecScoreMath.end(), // 输入数据的范围
vecScoreEng.begin(), // 第二个输入数据的起始位置
vecScore.begin(), //目标容器的起始位置
add ); // 对数据进行处理的操作函数
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值