算法——不修改序列的操作(copy、move、transform、remove、replace)
- 一、分类
- 二、修改序列的操作
- 三、copy
- 四、move
- 五、remove、remove_if
- 六、fill、transform、replace、replace_if、reverse
一、分类
根据网站https://www.apiref.com/cpp-zh/cpp/header.html显示,头文件<algorithm>提供的算法如下图。
- 常用的分类
- 不修改序列的操作
- 修改序列的操作
- 排序操作
- 集合操作
- 头文件
#include <algorithm>
二、修改序列的操作
项目 | Value |
---|---|
copy() | 将一系列元素复制到新位置。 |
copy_if() | 如果谓词对值返回true,则将一系列元素复制到新位置。 |
move() | 将某一范围的元素移动到一个新的位置 |
fill() | 将一个给定值复制赋值给一个范围内的每个元素 |
transform | 将一个函数应用于某一范围的各个元素,并在目标范围存储结果 |
remove() | 移除满足特定判别标准的元素 |
remove_if() | 移除满足特定判别标准的元素 |
replace | 将所有满足特定判别标准的值替换为另一个值 |
replace_if | 将所有满足特定判别标准的值替换为另一个值 |
reverse | 逆转范围中的元素顺序 |
三、copy
C++98
template <class InputIterator, class OutputIterator>
OutputIterator copy (InputIterator first, InputIterator last, OutputIterator result);
参数 (Parameters)
first - 将迭代器输入到序列中的初始位置。
last - 将迭代器输入到序列中的最终位置。
result - 将迭代器输出到新序列中的初始位置。
返回值
返回到目标范围末尾的迭代器,其中元素已被复制。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main(void) {
vector<int> v1 = { 1, 2, 3, 4, 5 };
vector<int> v2(5);
copy(v1.begin(), v1.end(), v2.begin());
cout << "Vector v2 contains following elements" << endl;
for (auto it = v2.begin(); it != v2.end(); ++it)
cout << *it << endl;
return 0;
}
输出
Vector v2 contains following elements
1
2
3
4
5
- copy_if() 算法可以从源序列复制使谓词返回 true 的元素,所以可以把它看作一个过滤器。前两个参数定义源序列的输入迭代器,第三个参数是指向目的序列的第一个位置的输出迭代器,第 4 个参数是一个谓词。会返回一个输出迭代器,它指向最后一个被复制元素的下一个位置。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
bool pre(int n) {
return (n % 2 != 0);
};
int main(void) {
vector<int> v1 = { 1, 2, 3, 4, 5 };
vector<int> v2(3);
copy_if(v1.begin(), v1.end(), v2.begin(), pre);
cout << "Following are the Odd numbers from vector" << endl;
for (auto it = v2.begin(); it != v2.end(); ++it)
cout << *it << endl;
return 0;
}
输出
Following are the Odd numbers from vector
1
3
5
四、move
-
move() 算法会将它的前两个输入迭代器参数指定的序列移到第三个参数定义的目的序列的开始位置,第三个参数必须是输出迭代器。这个算法返回的迭代器指向最后一个被移动到目的序列的元素的下一个位置。
-
这是一个移动操作,因此无法保证在进行这个操作之后,输入序列仍然保持不变;源元素仍然会存在,但它们的值可能不再相同了,因此在移动之后,就不应该再使用它们。如果源序列可以被替换或破坏,就可以选择使用 move() 算法。如果不想扰乱源序列,可以使用 copy() 算法。
#include <iostream>
#include <vector>
using namespace std;
void test1()
{
cout << "==============test1==============\n";
std::vector<int> v1 = { 1,2,3 ,4,5 };
std::vector<int> v2 = { 6,7,8 };
// 建议的用法
std::move(v1.begin(), v1.end(), std::back_inserter(v2));
cout << "v1.size()=" << v1.size() << endl;
cout << "v2.size()=" << v2.size() << endl;
for (auto t : v2) std::cout << t;
cout << endl;
for (auto t : v1) std::cout << t;
}
void test2()
{
cout << "\n==============test2==============\n";
std::vector<int> v1 = { 1,2,3 ,4,5 };
std::vector<int> v2 = { 6,7,8 };
// 建议的用法
v2 = std::move(v1);
cout << "v1.size() = " << v1.size() << endl;
cout << "v2.size() = " << v2.size() << endl;
for (auto t : v2) std::cout << t;
}
void test3()
{
cout << "\n==============test3==============\n";
std::vector<int> v1 = { 1,2,3 };
std::vector<int> v2 = { 6,7,8 ,9 };
// 不建议使用,v1长度大于v2时,会报错的
std::move(v1.begin(), v1.end(), v2.begin());
cout << "v1.size() = " << v1.size() << endl;
cout << "v2.size() = " << v2.size() << endl;
}
int main()
{
test1();
test2();
test3();
}
输出
==============test1==============
v1.size()=5
v2.size()=8
67812345
12345
==============test2==============
v1.size() = 0
v2.size() = 5
12345
==============test3==============
v1.size() = 3
v2.size() = 4
#include <iostream>
#include <vector>
#include <list>
#include <iterator>
#include <thread>
#include <chrono>
void f(int n)
{
std::this_thread::sleep_for(std::chrono::seconds(n));
std::cout << "thread " << n << " ended" << '\n';
}
int main()
{
std::vector<std::thread> v;
v.emplace_back(f, 1);
v.emplace_back(f, 2);
v.emplace_back(f, 3);
std::list<std::thread> l;
// copy() 无法编译,因为 std::thread 不可复制
std::move(v.begin(), v.end(), std::back_inserter(l));
for (auto& t : l) t.join();
}
输出
thread 1 ended
thread 2 ended
thread 3 ended
五、remove、remove_if
移除操作不会改变被“移除”元素的序列的元素个数。
有 4 种移除算法:
- remove() 可以从它的前两个正向迭代器参数指定的序列中移除和第三个参数相等的对象。基本上每个元素都是通过用它后面的元素覆盖它来实现移除的。它会返回一个指向新的最后一个元素之后的位置的迭代器。
remove_if() 可以从前两个正向迭代器指定的序列中移除能够使作为第三个参数的谓词返回 true 的元素。 - 官网的代码很好,大家可以分析下,很有意思的。
#include <algorithm>
#include <cctype>
#include <iostream>
#include <string>
#include <string_view>
int main()
{
std::string str1 = "Text with some spaces";
auto noSpaceEnd = std::remove(str1.begin(), str1.end(), ' ');
// The spaces are removed from the string only logically.
// Note, we use view, the original string is still not shrunk:
std::cout << std::string_view(str1.begin(), noSpaceEnd)
<< " size: " << str1.size() << '\n';
str1.erase(noSpaceEnd, str1.end());
// The spaces are removed from the string physically.
std::cout << str1 << " size: " << str1.size() << '\n';
std::string str2 = "Text\n with\tsome \t whitespaces\n\n";
auto f = [](unsigned char x) { return std::isspace(x); };
str2.erase(std::remove_if(str2.begin(),str2.end(), f),str2.end());
std::cout << str2 << '\n';
}
输出
Textwithsomespaces size: 23
Textwithsomespaces size: 18
Textwithsomewhitespaces
六、fill、transform、replace、replace_if、reverse
#include <iostream>
#include <vector>
#include<string>
#include <algorithm>
#include <random>
using namespace std;
void test1()
{
cout << "\n==============test1==============\n";
vector<int> v(3);
fill(v.begin(), v.end(), 1);
for (auto it = v.begin(); it != v.end(); ++it)
cout << *it << endl;
}
void test2()
{
cout << "\n==============test2==============\n";
std::vector<double> v1{ 20.0, 0.0, 3.2, 100.0 };
std::vector<double> v2(v1.size());
auto f = [](double temp) { return temp / 2 + 1; };
std::transform(std::begin(v1), std::end(v1), std::rbegin(v2), f);
for (auto it = v2.begin(); it != v2.end(); ++it)
cout << *it << endl;
}
void test3()
{
cout << "\n==============test3==============\n";
std::vector<int> v{ 10, 11, 10, -6 };
std::replace(std::begin(v), std::end(v), 10, 99);
for (auto it = v.begin(); it != v.end(); ++it)
cout << *it << endl;
}
void test4()
{
cout << "\n==============test4==============\n";
string str{ "This is a good choice !" };
std::replace_if(std::begin(str), std::end(str), [](char ch) {return std::isspace(ch); }, '_');
//Result:This_is_a_good_choice!
cout << str;
}
void test5()
{
cout << "\n==============test5==============\n";
std::vector<int> v{ 1,2,3 };
std::reverse(std::begin(v), std::end(v));
for (auto e : v) std::cout << e;
}
int main(void) {
test1();
test2();
test3();
test4();
test5();
return 0;
}
输出
参考
1、C++ STL 容器库 中文文档
2、STL教程:C++ STL快速入门
3、https://www.apiref.com/cpp-zh/cpp/header.html
4、https://en.cppreference.com/w/cpp/header
5、WIKI教程_C ++标准库_C++ Library - <algorithm>