C++ 修改序列操作
在C++的STL(标准模板库)中,修改序列操作是一组能够修改容器内元素的算法。这些算法可以帮助我们对序列进行复制、替换、填充、删除等操作,大大简化了对容器内元素的处理。本文将详细介绍这些修改序列操作的使用方法和实际应用场景。
修改序列操作概述
修改序列操作主要位于<algorithm>
头文件中,它们接受迭代器作为参数,对迭代器指向的序列进行修改。常用的修改序列操作包括:
- 复制操作:
copy
,copy_if
,copy_n
,copy_backward
等 - 替换操作:
replace
,replace_if
,replace_copy
等 - 填充操作:
fill
,fill_n
,generate
,generate_n
等 - 移除操作:
remove
,remove_if
,remove_copy
等 - 独特化操作:
unique
,unique_copy
等 - 交换和重排操作:
swap
,iter_swap
,swap_ranges
,reverse
,rotate
等
复制操作
复制操作是最基础的序列修改操作,用于将一个序列的元素复制到另一个序列中。
copy
copy
算法将一个范围内的元素复制到另一个位置:
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> source = {1, 2, 3, 4, 5};
std::vector<int> destination(5);
// 将source中的元素复制到destination中
std::copy(source.begin(), source.end(), destination.begin());
// 打印destination中的元素
for (int num : destination) {
std::cout << num << " ";
}
// 输出: 1 2 3 4 5
return 0;
}
copy_if
copy_if
算法根据指定条件将元素复制到另一个位置:
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> source = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
std::vector<int> destination;
// 预留空间以提高效率
destination.reserve(source.size());
// 将source中的偶数复制到destination中
std::copy_if(source.begin(), source.end(),
std::back_inserter(destination),
[](int n) { return n % 2 == 0; });
// 打印destination中的元素
for (int num : destination) {
std::cout << num << " ";
}
// 输出: 2 4 6 8 10
return 0;
}
使用std::back_inserter
创建一个插入迭代器,每次赋值时都会在容器末尾插入新元素,避免了预先分配空间大小的问题。
替换操作
替换操作用于将序列中满足特定条件的元素替换为新值。
replace
replace
算法将序列中所有等于给定值的元素替换为新值:
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> numbers = {1, 2, 3, 2, 5, 2, 7, 8};
// 将所有2替换为20
std::replace(numbers.begin(), numbers.end(), 2, 20);
// 打印替换后的结果
for (int num : numbers) {
std::cout << num << " ";
}
// 输出: 1 20 3 20 5 20 7 8
return 0;
}
replace_if
replace_if
算法将序列中所有满足谓词条件的元素替换为新值:
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8};
// 将所有偶数替换为0
std::replace_if(numbers.begin(), numbers.end(),
[](int n) { return n % 2 == 0; }, 0);
// 打印替换后的结果
for (int num : numbers) {
std::cout << num << " ";
}
// 输出: 1 0 3 0 5 0 7 0
return 0;
}
填充操作
填充操作用于将特定值或者根据生成器函数的结果填充到序列中。
fill
fill
算法将指定的值填充到指定范围的每个元素:
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> numbers(5);
// 将向量中的所有元素设置为10
std::fill(numbers.begin(), numbers.end(), 10);
// 打印结果
for (int num : numbers) {
std::cout << num << " ";
}
// 输出: 10 10 10 10 10
return 0;
}
generate
generate
算法将生成器函数的结果填充到指定范围的每个元素:
#include <iostream>
#include <vector>
#include <algorithm>
#include <random>
int main() {
std::vector<int> numbers(5);
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(1, 100);
// 用随机数填充向量
std::generate(numbers.begin(), numbers.end(), [&]() { return dis(gen); });
// 打印结果
for (int num : numbers) {
std::cout << num << " ";
}
// 输出: 例如 45 23 78 91 12 (随机数)
return 0;
}
移除操作
移除操作用于从序列中移除满足特定条件的元素。
STL的移除操作并不会真正从容器中删除元素,它们只是将要保留的元素移动到序列的前部,并返回一个迭代器指向新的逻辑结尾。要真正删除元素,需要配合容器的erase
方法使用。
remove
remove
算法移除所有等于给定值的元素:
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> numbers = {1, 2, 3, 2, 5, 2, 7, 8};
// 移除所有值为2的元素
auto new_end = std::remove(numbers.begin(), numbers.end(), 2);
// 截断向量,真正删除元素
numbers.erase(new_end, numbers.end());
// 打印结果
for (int num : numbers) {
std::cout << num << " ";
}
// 输出: 1 3 5 7 8
return 0;
}
remove_if
remove_if
算法移除所有满足谓词条件的元素:
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8};
// 移除所有偶数
auto new_end = std::remove_if(numbers.begin(), numbers.end(),
[](int n) { return n % 2 == 0; });
// 截断向量,真正删除元素
numbers.erase(new_end, numbers.end());
// 打印结果
for (int num : numbers) {
std::cout << num << " ";
}
// 输出: 1 3 5 7
return 0;
}
独特化操作
独特化操作用于移除序列中的重复元素,使序列中的每个元素都是唯一的。
unique
unique
算法移除序列中连续的重复元素(序列应该事先已排序):
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> numbers = {1, 2, 2, 3, 3, 3, 4, 5, 5};
// 移除连续重复元素
auto new_end = std::unique(numbers.begin(), numbers.end());
// 截断向量
numbers.erase(new_end, numbers.end());
// 打印结果
for (int num : numbers) {
std::cout << num << " ";
}
// 输出: 1 2 3 4 5
return 0;
}
unique
只能移除相邻的重复元素。如果想要移除所有重复元素,先使用sort
排序,再使用unique
。
交换和重排操作
交换和重排操作用于改变序列中元素的顺序。
swap
swap
算法交换两个变量的值:
#include <iostream>
#include <algorithm>
int main() {
int a = 5, b = 10;
std::cout << "Before swap: a = " << a << ", b = " << b << std::endl;
// 交换a和b的值
std::swap(a, b);
std::cout << "After swap: a = " << a << ", b = " << b << std::endl;
// 输出: After swap: a = 10, b = 5
return 0;
}
reverse
reverse
算法将指定范围内的元素顺序反转:
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
// 反转序列
std::reverse(numbers.begin(), numbers.end());
// 打印结果
for (int num : numbers) {
std::cout << num << " ";
}
// 输出: 5 4 3 2 1
return 0;
}
rotate
rotate
算法将序列中的元素进行旋转:
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
// 将序列旋转,使第3个元素(索引2)成为第一个元素
std::rotate(numbers.begin(), numbers.begin() + 2, numbers.end());
// 打印结果
for (int num : numbers) {
std::cout << num << " ";
}
// 输出: 3 4 5 1 2
return 0;
}
实际应用案例
案例1:数据过滤与转换
假设我们有一个学生成绩数据库,需要筛选出所有及格的学生(分数≥60)并将他们的分数提高5分作为奖励:
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
struct Student {
std::string name;
int score;
Student(const std::string& n, int s) : name(n), score(s) {}
};
int main() {
// 学生数据
std::vector<Student> allStudents = {
{"Alice", 85}, {"Bob", 45}, {"Charlie", 90},
{"David", 55}, {"Eve", 75}, {"Frank", 60}
};
std::vector<Student> passedStudents;
// 筛选及格学生(分数≥60)
std::copy_if(allStudents.begin(), allStudents.end(),
std::back_inserter(passedStudents),
[](const Student& s) { return s.score >= 60; });
// 给及格学生加5分奖励(最高不超过100分)
std::for_each(passedStudents.begin(), passedStudents.end(),
[](Student& s) { s.score = std::min(s.score + 5, 100); });
// 打印结果
std::cout << "及格学生名单(加分后):\n";
for (const auto& student : passedStudents) {
std::cout << student.name << ": " << student.score << std::endl;
}
return 0;
}
输出:
及格学生名单(加分后):
Alice: 90
Charlie: 95
Eve: 80
Frank: 65
案例2:文本处理
假设我们需要处理一段文本,移除所有标点符号并将所有单词转换为小写:
#include <iostream>
#include <string>
#include <algorithm>
#include <cctype>
int main() {
std::string text = "Hello, World! This is a C++ Program. Isn't it AMAZING?";
// 将所有大写字母转换为小写
std::transform(text.begin(), text.end(), text.begin(),
[](unsigned char c) { return std::tolower(c); });
// 移除标点符号和空格
auto newEnd = std::remove_if(text.begin(), text.end(),
[](unsigned char c) {
return std::ispunct(c) || std::isspace(c);
});
text.erase(newEnd, text.end());
std::cout << "处理后的文本:" << text << std::endl;
return 0;
}
输出:
处理后的文本:helloworldthisisacprogramisntitamazing
总结
C++ STL提供的修改序列操作是处理容器内元素的强大工具:
- 复制操作(如
copy
,copy_if
)可以方便地复制元素。 - 替换操作(如
replace
,replace_if
)可以替换满足特定条件的元素。 - 填充操作(如
fill
,generate
)可以用特定值填充序列。 - 移除操作(如
remove
,remove_if
)可以移除满足特定条件的元素。 - 独特化操作(如
unique
)可以移除重复元素。 - 交换和重排操作(如
swap
,reverse
,rotate
)可以改变元素位置。
这些算法大大简化了元素处理的代码量,提高了代码的可读性和效率。熟练掌握这些操作将让你的C++编程更加高效。
练习题
- 编写一个程序,使用
replace_if
将向量中所有负数替换为0。 - 使用
remove_copy_if
和back_inserter
,将一个字符串中的元音字母复制到另一个字符串中。 - 编写一个程序,使用
unique
移除已排序数组中的所有重复元素。 - 使用
rotate
实现一个简单的循环队列。
参考资源
- C++ Reference - Modifying sequence operations
- C++ STL Algorithms Tutorial
- Stroustrup, Bjarne. "The C++ Programming Language", 4th Edition.