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

C++学习:六个月从基础到就业——STL算法(一) 基础与查找算法

C++学习:六个月从基础到就业——STL算法(一) 基础与查找算法

本文是我C++学习之旅系列的第二十五篇技术文章,也是第二阶段"C++进阶特性"的第三篇,主要介绍C++ STL算法库的基础知识与查找类算法。查看完整系列目录了解更多内容。

引言

在前面的两篇文章中,我们深入探讨了STL的容器和迭代器。今天,我们将开始学习STL的第三个核心组件——算法库,这是C++标准库的强大功能之一。为了便于学习和理解,我将把STL算法库的内容分成四个部分:

  1. 基础与查找算法(本文)
  2. 排序与变序算法
  3. 数值与集合算法
  4. 实际应用案例

本文作为STL算法系列的第一篇,将介绍算法库的基础知识,以及常用的查找和非修改序列算法。

STL算法库概述

什么是STL算法库?

STL算法库是C++标准模板库的一部分,提供了一系列用于处理容器元素的通用函数模板。这些算法通过迭代器操作容器中的元素,使得相同的算法可以应用于不同类型的容器,实现了代码的高度复用。

STL算法库主要位于头文件<algorithm>中,还有部分数值算法位于<numeric>头文件中。

STL算法库的特点

  1. 泛型设计:通过模板实现,可以处理任何类型的数据
  2. 高效实现:经过优化的算法,提供良好的性能
  3. 表达力强:使用合适的算法可以用少量代码表达复杂的操作
  4. 与迭代器紧密结合:通过迭代器接口操作容器,实现容器与算法的解耦

算法分类

STL算法库中的算法可以大致分为以下几类:

  1. 非修改序列操作:不改变元素值的算法,如查找、计数等
  2. 修改序列操作:改变元素值或位置的算法,如复制、替换、删除等
  3. 排序相关算法:用于排序和与排序相关的操作,如排序、合并、二分查找等
  4. 数值算法:用于进行数值计算的算法,如求和、乘积等
  5. 集合操作:操作有序集合的算法,如并集、交集等

非修改序列算法

非修改序列算法是指那些不会修改容器元素值的算法,它们主要用于元素的查找、计数、比较等操作。

遍历算法:std::for_eachstd::for_each_n

for_each是一个非常实用的算法,它对区间内的每个元素应用指定的函数。

#include <iostream>
#include <vector>
#include <algorithm>// 打印元素的函数
void print(int n) {std::cout << n << " ";
}int main() {std::vector<int> nums = {1, 2, 3, 4, 5};// 使用函数指针std::cout << "使用函数指针: ";std::for_each(nums.begin(), nums.end(), print);std::cout << std::endl;// 使用lambda表达式std::cout << "使用lambda表达式: ";std::for_each(nums.begin(), nums.end(), [](int n) {std::cout << n * n << " "; // 打印平方值});std::cout << std::endl;// 使用for_each_n (C++17)std::cout << "使用for_each_n处理前3个元素: ";std::for_each_n(nums.begin(), 3, print);std::cout << std::endl;return 0;
}

输出:

使用函数指针: 1 2 3 4 5 
使用lambda表达式: 1 4 9 16 25 
使用for_each_n处理前3个元素: 1 2 3 

for_each算法非常灵活,可以用于执行任何操作,包括修改元素(如果传递的是非const引用)。但要注意,如果你只是想对每个元素进行简单操作,考虑使用范围for循环(C++11引入)可能更加清晰。

查找算法: std::findstd::find_ifstd::find_if_not

查找算法用于在容器中定位特定的元素。

#include <iostream>
#include <vector>
#include <algorithm>
#include <string>struct Person {std::string name;int age;Person(std::string n, int a) : name(std::move(n)), age(a) {}
};int main() {std::vector<int> nums = {10, 20, 30, 40, 50};// 查找特定值auto it1 = std::find(nums.begin(), nums.end(), 30);if (it1 != nums.end()) {std::cout << "找到值30,位置: " << std::distance(nums.begin(), it1) << std::endl;} else {std::cout << "未找到值30" << std::endl;}// 查找符合条件的元素auto it2 = std::find_if(nums.begin(), nums.end(), [](int n) {return n > 35;});if (it2 != nums.end()) {std::cout << "找到第一个大于35的值: " << *it2 << std::endl;}// 查找不符合条件的元素auto it3 = std::find_if_not(nums.begin(), nums.end(), [](int n) {return n < 30;});if (it3 != nums.end()) {std::cout << "找到第一个不小于30的值: " << *it3 << std::endl;}// 在结构体向量中查找std::vector<Person> people = {Person("Alice", 30),Person("Bob", 25),Person("Charlie", 35),Person("David", 28)};// 查找名字为Bob的人auto personIt = std::find_if(people.begin(), people.end(), [](const Person& p) {return p.name == "Bob";});if (personIt != people.end()) {std::cout << "找到Bob,他的年龄是: " << personIt->age << std::endl;}return 0;
}

输出:

找到值30,位置: 2
找到第一个大于35的值: 40
找到第一个不小于30的值: 30
找到Bob,他的年龄是: 25

查找序列:std::searchstd::find_endstd::find_first_of

这些算法用于查找子序列或元素集合:

#include <iostream>
#include <vector>
#include <algorithm>
#include <string>void printPosition(const std::string& name, const std::vector<int>& v, std::vector<int>::iterator it) {if (it != v.end()) {std::cout << name << "找到,位置: " << std::distance(v.begin(), it) << std::endl;} else {std::cout << name << "未找到" << std::endl;}
}int main() {std::vector<int> haystack = {1, 2, 3, 4, 5, 1, 2, 3, 4, 5};std::vector<int> needle = {2, 3, 4};// search - 查找第一次出现的子序列auto it1 = std::search(haystack.begin(), haystack.end(), needle.begin(), needle.end());printPosition("search: 子序列", haystack, it1);// find_end - 查找最后一次出现的子序列auto it2 = std::find_end(haystack.begin(), haystack.end(), needle.begin(), needle.end());printPosition("find_end: 子序列", haystack, it2);// find_first_of - 查找任何一个元素集合中的元素首次出现std::vector<int> anyOf = {5, 10, 15};auto it3 = std::find_first_of(haystack.begin(), haystack.end(), anyOf.begin(), anyOf.end());printPosition("find_first_of: 任何一个元素", haystack, it3);// 使用谓词版本auto it4 = std::search(haystack.begin(), haystack.end(), needle.begin(), needle.end(),[](int a, int b) { return std::abs(a - b) <= 1; }); // 允许值相差1printPosition("search(谓词): 相似子序列", haystack, it4);// 使用Boyer-Moore搜索(C++17)// 需要支持C++17的编译器/*auto it5 = std::search(haystack.begin(), haystack.end(), std::boyer_moore_searcher(needle.begin(), needle.end()));printPosition("Boyer-Moore search: 子序列", haystack, it5);*/// 在字符串中搜索std::string text = "The quick brown fox jumps over the lazy dog";std::string pattern = "fox";auto strIt = std::search(text.begin(), text.end(), pattern.begin(), pattern.end());if (strIt != text.end()) {std::cout << "找到字符串 \"" << pattern << "\" 在位置: " << std::distance(text.begin(), strIt) << std::endl;}return 0;
}

输出:

search: 子序列找到,位置: 1
find_end: 子序列找到,位置: 6
find_first_of: 任何一个元素找到,位置: 4
search(谓词): 相似子序列找到,位置: 0
找到字符串 "fox" 在位置: 16

这些查找算法在处理复杂数据集时非常有用,特别是当我们需要查找特定模式或元素集合时。

计数算法:std::countstd::count_if

这些算法用于计算满足特定条件的元素数量:

#include <iostream>
#include <vector>
#include <algorithm>
#include <string>int main() {std::vector<int> nums = {1, 2, 3, 2, 4, 2, 5, 6, 2, 7};// 计算特定值的出现次数int count2 = std::count(nums.begin(), nums.end(), 2);std::cout << "数字2出现的次数: " << count2 << std::endl;// 计算符合条件的元素数量int countEven = std::count_if(nums.begin(), nums.end(), [](int n) {return n % 2 == 0;});std::cout << "偶数的数量: " << countEven << std::endl;// 在字符串中计数std::string text = "The quick brown fox jumps over the lazy dog";// 计算空格数量int spaces = std::count(text.begin(), text.end(), ' ');std::cout << "空格数量: " << spaces << std::endl;// 计算元音字母数量auto isVowel = [](char c) {c = std::tolower(c);return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u';};int vowels = std::count_if(text.begin(), text.end(), isVowel);std::cout << "元音字母数量: " << vowels << std::endl;return 0;
}

输出:

数字2出现的次数: 4
偶数的数量: 5
空格数量: 8
元音字母数量: 11

比较算法:std::equalstd::mismatchstd::lexicographical_compare

这些算法用于比较序列:

#include <iostream>
#include <vector>
#include <algorithm>
#include <string>int main() {std::vector<int> v1 = {1, 2, 3, 4, 5};std::vector<int> v2 = {1, 2, 3, 4, 5};std::vector<int> v3 = {1, 2, 3, 5, 4};// 检查两个序列是否相等bool equal12 = std::equal(v1.begin(), v1.end(), v2.begin());bool equal13 = std::equal(v1.begin(), v1.end(), v3.begin());std::cout << "v1和v2相等? " << (equal12 ? "是" : "否") << std::endl;std::cout << "v1和v3相等? " << (equal13 ? "是" : "否") << std::endl;// 找出第一个不匹配的位置auto [it1, it2] = std::mismatch(v1.begin(), v1.end(), v3.begin());if (it1 != v1.end()) {std::cout << "第一个不匹配位置: " << std::distance(v1.begin(), it1);std::cout << ", v1中的值: " << *it1 << ", v3中的值: " << *it2 << std::endl;}// 比较序列的字典序bool less13 = std::lexicographical_compare(v1.begin(), v1.end(), v3.begin(), v3.end());bool less31 = std::lexicographical_compare(v3.begin(), v3.end(), v1.begin(), v1.end());std::cout << "v1字典序小于v3? " << (less13 ? "是" : "否") << std::endl;std::cout << "v3字典序小于v1? " << (less31 ? "是" : "否") << std::endl;// 字符串比较std::string s1 = "apple";std::string s2 = "banana";bool lessStr = std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end());std::cout << "apple字典序小于banana? " << (lessStr ? "是" : "否") << std::endl;// 使用C++17的加强版equal (检查长度不同的序列)std::vector<int> v4 = {1, 2, 3};// 在C++17之前需要手动检查大小bool equal14 = v1.size() == v4.size() && std::equal(v1.begin(), v1.end(), v4.begin());// C++17可以直接提供第二个范围的结束迭代器// bool equal14_cpp17 = std::equal(v1.begin(), v1.end(), v4.begin(), v4.end());std::cout << "v1和v4相等? " << (equal14 ? "是" : "否") << std::endl;return 0;
}

输出:

v1和v2相等? 是
v1和v3相等? 否
第一个不匹配位置: 3, v1中的值: 4, v3中的值: 5
v1字典序小于v3? 是
v3字典序小于v1? 否
apple字典序小于banana? 是
v1和v4相等? 否

查询匹配条件:std::all_ofstd::any_ofstd::none_of

这些算法用于检查序列中的元素是否满足特定条件:

#include <iostream>
#include <vector>
#include <algorithm>int main() {std::vector<int> v1 = {2, 4, 6, 8, 10};std::vector<int> v2 = {1, 3, 5, 7, 9};std::vector<int> v3 = {1, 2, 3, 4, 5};// 检查是否所有元素都是偶数bool allEven1 = std::all_of(v1.begin(), v1.end(), [](int n) { return n % 2 == 0; });bool allEven2 = std::all_of(v2.begin(), v2.end(), [](int n) { return n % 2 == 0; });std::cout << "v1中所有元素都是偶数? " << (allEven1 ? "是" : "否") << std::endl;std::cout << "v2中所有元素都是偶数? " << (allEven2 ? "是" : "否") << std::endl;// 检查是否存在偶数bool anyEven2 = std::any_of(v2.begin(), v2.end(), [](int n) { return n % 2 == 0; });bool anyEven3 = std::any_of(v3.begin(), v3.end(), [](int n) { return n % 2 == 0; });std::cout << "v2中存在偶数? " << (anyEven2 ? "是" : "否") << std::endl;std::cout << "v3中存在偶数? " << (anyEven3 ? "是" : "否") << std::endl;// 检查是否不存在偶数bool noneEven2 = std::none_of(v2.begin(), v2.end(), [](int n) { return n % 2 == 0; });std::cout << "v2中不存在偶数? " << (noneEven2 ? "是" : "否") << std::endl;// 实际应用:验证用户输入std::vector<std::string> userInputs = {"123", "abc", "456", "xyz"};bool allDigits = std::all_of(userInputs.begin(), userInputs.end(), [](const std::string& s) {return std::all_of(s.begin(), s.end(), ::isdigit);});std::cout << "所有输入都是数字? " << (allDigits ? "是" : "否") << std::endl;// 找出包含数字的输入auto hasDigit = [](const std::string& s) {return std::any_of(s.begin(), s.end(), ::isdigit);};std::cout << "包含数字的输入: ";for (const auto& input : userInputs) {if (hasDigit(input)) {std::cout << input << " ";}}std::cout << std::endl;return 0;
}

输出:

v1中所有元素都是偶数? 是
v2中所有元素都是偶数? 否
v2中存在偶数? 否
v3中存在偶数? 是
v2中不存在偶数? 是
所有输入都是数字? 否
包含数字的输入: 123 456 

专用查找算法:std::adjacent_findstd::search_n

adjacent_find用于查找相邻的相同元素,而search_n用于查找连续重复的元素:

#include <iostream>
#include <vector>
#include <algorithm>
#include <string>int main() {std::vector<int> v1 = {1, 3, 3, 5, 7, 9, 9};// 查找相邻重复元素auto it1 = std::adjacent_find(v1.begin(), v1.end());if (it1 != v1.end()) {std::cout << "找到相邻重复元素: " << *it1 << ", 位置: " << std::distance(v1.begin(), it1) << std::endl;}// 使用自定义条件查找相邻元素auto it2 = std::adjacent_find(v1.begin(), v1.end(), [](int a, int b) {return b - a > 1;  // 查找两个元素之间的差大于1});if (it2 != v1.end()) {std::cout << "找到差大于1的相邻元素: " << *it2 << " 和 " << *(it2 + 1)<< ", 位置: " << std::distance(v1.begin(), it2) << std::endl;}// 查找连续的n个相同元素std::vector<int> v2 = {1, 2, 3, 3, 3, 4, 5, 5, 6};auto it3 = std::search_n(v2.begin(), v2.end(), 3, 3);  // 查找3个连续的3if (it3 != v2.end()) {std::cout << "找到3个连续的3,起始位置: " << std::distance(v2.begin(), it3) << std::endl;}// 使用谓词版本的search_nstd::vector<double> v3 = {1.1, 1.2, 3.5, 3.4, 3.6, 5.5};auto it4 = std::search_n(v3.begin(), v3.end(), 3, 3.0, [](double val, double target) {return std::abs(val - target) <= 1.0;  // 找出三个连续的接近3的值});if (it4 != v3.end()) {std::cout << "找到3个连续接近3的值: ";for (int i = 0; i < 3; ++i) {std::cout << *(it4 + i) << " ";}std::cout << ",起始位置: " << std::distance(v3.begin(), it4) << std::endl;}// 在字符串中的应用std::string text = "Hello, how   are you doing?";auto it5 = std::adjacent_find(text.begin(), text.end(), [](char a, char b) {return std::isspace(a) && std::isspace(b);});if (it5 != text.end()) {std::cout << "找到连续空格,位置: " << std::distance(text.begin(), it5) << std::endl;}return 0;
}

输出:

找到相邻重复元素: 3, 位置: 1
找到差大于1的相邻元素: 3 和 5, 位置: 1
找到3个连续的3,起始位置: 2
找到3个连续接近3的值: 3.5 3.4 3.6 ,起始位置: 2
找到连续空格,位置: 11

简单修改序列算法

虽然这篇文章主要关注查找和非修改序列算法,但我们也会介绍几个简单的修改序列算法,它们经常与查找算法结合使用。

生成与填充:std::fillstd::fill_nstd::generate

这些算法用于填充或生成序列中的数据:

#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
#include <random>template<typename T>
void printVector(const std::vector<T>& vec, const std::string& name) {std::cout << name << ": ";for (const auto& item : vec) {std::cout << item << " ";}std::cout << std::endl;
}int main() {// 使用fill填充向量std::vector<int> v1(10);std::fill(v1.begin(), v1.end(), 5);printVector(v1, "fill(5)");// 使用fill_n填充部分向量std::vector<int> v2(10, 0);  // 初始化为10个0std::fill_n(v2.begin() + 2, 5, 7);  // 从索引2开始填充5个7printVector(v2, "fill_n(7)");// 使用generate生成序列std::vector<int> v3(10);int value = 1;std::generate(v3.begin(), v3.end(), [&value]() { return value++; });printVector(v3, "generate(递增)");// 使用generate_n生成部分序列std::vector<int> v4(10, 0);value = 10;std::generate_n(v4.begin() + 3, 5, [&value]() { return value--; });printVector(v4, "generate_n(递减)");// 生成随机数std::vector<int> v5(10);std::random_device rd;std::mt19937 gen(rd());std::uniform_int_distribution<> dis(1, 100);std::generate(v5.begin(), v5.end(), [&]() { return dis(gen); });printVector(v5, "随机数");// 使用iota填充递增序列(来自<numeric>)std::vector<int> v6(10);std::iota(v6.begin(), v6.end(), 100);  // 从100开始的递增序列printVector(v6, "iota(从100开始)");return 0;
}

输出(随机数部分将有所不同):

fill(5): 5 5 5 5 5 5 5 5 5 5 
fill_n(7): 0 0 7 7 7 7 7 0 0 0 
generate(递增): 1 2 3 4 5 6 7 8 9 10 
generate_n(递减): 0 0 0 10 9 8 7 6 0 0 
随机数: 37 59 88 7 21 42 99 63 84 45 
iota(从100开始): 100 101 102 103 104 105 106 107 108 109 

转换元素:std::transform

transform算法用于将函数应用于序列中的元素,并将结果存储在另一序列中:

#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <cctype>template<typename T>
void printVector(const std::vector<T>& vec, const std::string& name) {std::cout << name << ": ";for (const auto& item : vec) {std::cout << item << " ";}std::cout << std::endl;
}int main() {std::vector<int> v1 = {1, 2, 3, 4, 5};// 一元操作 - 计算平方std::vector<int> squares(v1.size());std::transform(v1.begin(), v1.end(), squares.begin(),[](int x) { return x * x; });printVector(squares, "平方值");// 使用transform修改原始向量std::transform(v1.begin(), v1.end(), v1.begin(),[](int x) { return x * 2; });printVector(v1, "乘以2");// 二元操作 - 将两个向量相加std::vector<int> v2 = {10, 20, 30, 40, 50};std::vector<int> sum(v1.size());std::transform(v1.begin(), v1.end(), v2.begin(), sum.begin(),[](int x, int y) { return x + y; });printVector(sum, "v1 + v2");// 字符转换 - 将字符串转换为大写std::string text = "Hello, World!";std::string upper(text.size(), ' ');std::transform(text.begin(), text.end(), upper.begin(),[](unsigned char c) { return std::toupper(c); });std::cout << "原文: " << text << std::endl;std::cout << "转换后: " << upper << std::endl;// 复杂对象变换struct Person {std::string name;int age;};std::vector<Person> people = {{"Alice", 25},{"Bob", 30},{"Charlie", 35},{"David", 40}};// 提取姓名std::vector<std::string> names(people.size());std::transform(people.begin(), people.end(), names.begin(),[](const Person& p) { return p.name; });std::cout << "姓名列表: ";for (const auto& name : names) {std::cout << name << " ";}std::cout << std::endl;// 计算每个人明年的年龄std::vector<int> nextYearAges(people.size());std::transform(people.begin(), people.end(), nextYearAges.begin(),[](const Person& p) { return p.age + 1; });std::cout << "明年的年龄: ";for (int age : nextYearAges) {std::cout << age << " ";}std::cout << std::endl;return 0;
}

输出:

平方值: 1 4 9 16 25 
乘以2: 2 4 6 8 10 
v1 + v2: 12 24 36 48 60 
原文: Hello, World!
转换后: HELLO, WORLD!
姓名列表: Alice Bob Charlie David 
明年的年龄: 26 31 36 41 

实际应用示例

让我们通过一个实际的例子,展示如何结合使用上述算法来解决实际问题。

文本分析应用

下面的例子展示了一个简单的文本分析应用,包括分词、统计词频、查找特定单词等功能:

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <sstream>
#include <map>
#include <cctype>
#include <iomanip>// 将文本转换为小写
std::string toLowerCase(const std::string& text) {std::string result;std::transform(text.begin(), text.end(), std::back_inserter(result),[](unsigned char c) { return std::tolower(c); });return result;
}// 删除标点符号和数字
std::string cleanText(const std::string& text) {std::string result;std::copy_if(text.begin(), text.end(), std::back_inserter(result),[](unsigned char c) { return std::isalpha(c) || std::isspace(c); });return result;
}// 分词函数
std::vector<std::string> tokenize(const std::string& text) {std::vector<std::string> tokens;std::istringstream iss(text);std::string token;while (iss >> token) {if (!token.empty()) {tokens.push_back(toLowerCase(token));}}return tokens;
}// 统计词频
std::map<std::string, int> countWordFrequency(const std::vector<std::string>& words) {std::map<std::string, int> frequency;for (const auto& word : words) {++frequency[word];}return frequency;
}int main() {// 示例文本std::string text = "This is a sample text. This text is used to demonstrate ""the power of STL algorithms for text analysis. ""Algorithms are powerful tools in C++ programming.";std::cout << "原始文本:\n" << text << std::endl << std::endl;// 1. 清理文本std::string cleanedText = cleanText(text);// 2. 分词std::vector<std::string> words = tokenize(cleanedText);std::cout << "单词数量: " << words.size() << std::endl << std::endl;// 3. 统计词频auto wordFreq = countWordFrequency(words);// 按频率排序std::vector<std::pair<std::string, int>> freqPairs(wordFreq.begin(), wordFreq.end());std::sort(freqPairs.begin(), freqPairs.end(),[](const auto& a, const auto& b) {return a.second > b.second;  // 按频率降序排序});// 打印词频结果(前10个)std::cout << "词频统计 (前10个):" << std::endl;int count = 0;for (const auto& [word, freq] : freqPairs) {std::cout << std::setw(15) << std::left << word << ": " << freq << std::endl;if (++count >= 10) break;}std::cout << std::endl;// 4. 查找特定单词std::string searchWord = "algorithms";auto it = std::find(words.begin(), words.end(), searchWord);if (it != words.end()) {int position = std::distance(words.begin(), it);std::cout << "单词 \"" << searchWord << "\" 在位置 " << position << " 找到" << std::endl;// 查找所有出现位置std::cout << "所有出现位置: ";int pos = 0;for (auto wordIt = words.begin(); wordIt != words.end(); ++wordIt, ++pos) {if (*wordIt == searchWord) {std::cout << pos << " ";}}std::cout << std::endl << std::endl;} else {std::cout << "未找到单词 \"" << searchWord << "\"" << std::endl << std::endl;}// 5. 查找最长和最短的单词auto [minIt, maxIt] = std::minmax_element(words.begin(), words.end(),[](const std::string& a, const std::string& b) {return a.length() < b.length();});std::cout << "最短的单词: " << *minIt << " (" << minIt->length() << " 个字符)" << std::endl;std::cout << "最长的单词: " << *maxIt << " (" << maxIt->length() << " 个字符)" << std::endl;std::cout << std::endl;// 6. 统计以特定字母开头的单词char startLetter = 't';int startsWithT = std::count_if(words.begin(), words.end(),[startLetter](const std::string& word) {return !word.empty() && std::tolower(word[0]) == startLetter;});std::cout << "以字母 '" << startLetter << "' 开头的单词数量: " << startsWithT << std::endl;// 7. 检查是否所有单词都小于20个字符bool allShort = std::all_of(words.begin(), words.end(),[](const std::string& word) {return word.length() < 20;});std::cout << "所有单词都少于20个字符? " << (allShort ? "是" : "否") << std::endl;return 0;
}

输出:

原始文本:
This is a sample text. This text is used to demonstrate the power of STL algorithms for text analysis. Algorithms are powerful tools in C++ programming.单词数量: 24词频统计 (前10个):
is             : 3
text           : 2
this           : 2
algorithms     : 2
of             : 2
a              : 1
sample         : 1
used           : 1
to             : 1
demonstrate    : 1单词 "algorithms" 在位置 12 找到
所有出现位置: 12 19 最短的单词: a (1 个字符)
最长的单词: demonstrate (11 个字符)以字母 't' 开头的单词数量: 4
所有单词都少于20个字符? 是

这个例子展示了如何使用STL算法来实现一个文本分析应用,包括文本清理、分词、词频统计、单词查找和文本特征分析等功能。这些操作在自然语言处理和信息检索等领域非常常见。

总结

在本文中,我们介绍了STL算法库的基础知识,并详细探讨了非修改序列算法和一些简单的修改序列算法,包括:

  1. 遍历算法:如for_eachfor_each_n
  2. 查找算法:如findfind_ifsearchfind_first_of
  3. 计数算法:如countcount_if
  4. 比较算法:如equalmismatchlexicographical_compare
  5. 条件检查算法:如all_ofany_ofnone_of
  6. 特殊查找算法:如adjacent_findsearch_n
  7. 简单修改序列算法:如fillgeneratetransform

通过实际应用示例,我们看到了这些算法如何结合使用来解决文本处理等实际问题。

这些算法为我们提供了强大的工具,使我们能够以简洁、高效的方式处理容器中的数据,而不必手动编写循环和条件判断代码。这不仅提高了代码的可读性和可维护性,也减少了出错的可能性。

在下一篇文章中,我们将继续探讨STL算法库中的排序和变序算法,包括sortstable_sortpartial_sort以及reverserotateshuffle等变序算法。

参考资源

  • C++ Reference - 详细的STL算法文档
  • 《C++标准库》by Nicolai M. Josuttis
  • 《Effective STL》by Scott Meyers
  • 《C++17 STL Cookbook》by Jacek Galowicz

这是我C++学习之旅系列的第二十五篇技术文章。查看完整系列目录了解更多内容。

如有任何问题或建议,欢迎在评论区留言交流!

相关文章:

C++学习:六个月从基础到就业——STL算法(一) 基础与查找算法

C学习&#xff1a;六个月从基础到就业——STL算法&#xff08;一&#xff09; 基础与查找算法 本文是我C学习之旅系列的第二十五篇技术文章&#xff0c;也是第二阶段"C进阶特性"的第三篇&#xff0c;主要介绍C STL算法库的基础知识与查找类算法。查看完整系列目录了解…...

springboot+vue 支付宝支付(沙箱方式,测试环境使用)

准备工作&#xff1a; 1 支付宝沙箱环境的公钥&#xff0c;私钥配置&#xff0c;查询等使用&#xff0c;如果是用自定义的方式&#xff0c;需要下生成公钥&#xff0c;私钥的工具&#xff0c;否则不需要 登录 - 支付宝 小程序文档 - 支付宝文档中心 2 本地测试时&…...

安装win11自带linux是报错:WslRegisterDistribution failed with error: 0x800701bcErr

确保系统设置中的“打开win11的子系统”已打钩 管理员身份运行cmd&#xff0c;并输入如下 然后再重启ubantu...

面试经历(一)雪花算法

uid生成方面 1&#xff1a;为什么用雪花算法 分布式ID的唯一性需要保证&#xff0c;同时需要做到 1&#xff1a;单调递增 2&#xff1a;确保安全&#xff0c;一个是要能体现出递增的单号&#xff0c;二一个不能轻易的被恶意爬出订单数量 3&#xff1a;含有时间戳 4&#…...

docker底层原理简述

前言 平时用docker很多&#xff0c;今天深入了解下docker原理层面的实现&#xff0c;包括docker核心概念&#xff0c;文件系统&#xff0c;资源隔离&#xff0c;网络通信等 参考文章&#xff1a; Docker底层原理&#xff08;图解秒懂史上最全&#xff09; - 疯狂创客圈 - 博…...

【6D位姿估计】Foundation Pose复现

主要参考 项目仓库README站内其他博文 注意事项 容器化部署不难&#xff0c;主要是部署docker本身会存在一些环境问题&#xff0c;重点关注访问外网的端口需要手动调整至与魔法相同&#xff0c;可以参考&#xff1a; docker部署在启动容器镜像后&#xff0c;需要注意镜像当前…...

TDengine 数据订阅设计

简介 数据订阅作为 TDengine 的一个核心功能&#xff0c;为用户提供了灵活获取所需数据的能力。通过深入了解其内部原理&#xff0c;用户可以更加有效地利用这一功能&#xff0c;满足各种实时数据处理和监控需求。 基本概念 主题 与 Kafka 一样&#xff0c;使用 TDengine 数…...

VMware Fusion Pro 13 Mac版虚拟机 安装Win11系统教程

Mac分享吧 文章目录 Win11安装完成&#xff0c;软件打开效果一、VMware安装Windows11虚拟机1️⃣&#xff1a;准备镜像2️⃣&#xff1a;创建虚拟机3️⃣&#xff1a;虚拟机设置4️⃣&#xff1a;安装虚拟机5️⃣&#xff1a;解决连不上网问题 安装完成&#xff01;&#xff0…...

高并发下单库存扣减异常?飞算 JavaAI 自动化生成分布式事务解决方案

在电商、旅游等行业业务量激增&#xff0c;高并发下单场景中&#xff0c;传统库存扣减方式弊端尽显。超卖问题因缺乏有效并发控制机制频发&#xff0c;多个订单同时访问库存数据&#xff0c;导致同一商品多次售出&#xff0c;订单无法履约引发客户投诉&#xff1b;同时&#xf…...

crictl 遇到报错 /run/containerd/containerd.sock: connect: permission denied

报错内容 crictl --runtime-endpoint unix:///run/containerd/containerd.sock logs CONTAINERID FATA[0000] validate service connection: validate CRI v1 runtime API for endpoint "unix:///run/containerd/containerd.sock": rpc error: code Unavailable de…...

CSS外边距合并现象

外边距合并&#xff08;Margin Collapsing&#xff09;是指在文档流中&#xff0c;两个或多个相邻元素的外边距&#xff08;margin&#xff09;会合并为一个外边距&#xff0c;其大小会取其中最大的外边距值 当两个相邻的兄弟元素之间没有其他内容&#xff08;如边框、内边距、…...

《深度神经网络之数据增强、模型保存、模型调用、学习率调整》

文章目录 前言一、数据增强1、什么是数据增强&#xff1f;2、数据增强的实现方法&#xff08;1&#xff09;几何变换翻转:旋转&#xff1a;平移&#xff1a; &#xff08;2&#xff09;颜色变换亮度调整&#xff1a;对比度调整&#xff1a;色彩抖动&#xff1a; &#xff08;3&…...

【Java学习笔记】random的使用

random使用方法 使用说明&#xff1a;返回的是(0<n<1)这个范围中的任意带正号的double值 代码实例 public class helloworld{public static void main(String[] args){System.out.println(Math.random());} }生成0-100中的任意数代码示例 public class Main {public …...

Redis的string类型使用

第一步&#xff1a;添加缓存 以若依岗位代码为例 一&#xff1a;首先从redis中查询岗位信息&#xff0c;如果查询到了则直接返回。 二&#xff1a;如果redis中没有数据&#xff0c;则直接从数据库中查询。查询后放到redis并返回 package com.ruoyi.system.service.impl;imp…...

AIGC架构与原理

AIGC&#xff08;AI Generated Content&#xff0c;人工智能生成内容&#xff09;的架构与原理 AIGC通过整合数据采集、模型训练、推理服务等模块&#xff0c;结合深度学习与生成对抗网络&#xff08;GAN&#xff09;等技术&#xff0c;实现从数据到内容的自动化生成。 一、AIG…...

安全复健|windows常见取证工具

写在前面&#xff1a; 此博客仅用于记录个人学习内容&#xff0c;学识浅薄&#xff0c;若有错误观点欢迎评论区指出。欢迎各位前来交流。&#xff08;部分材料来源网络&#xff0c;若有侵权&#xff0c;立即删除&#xff09; 取证 01系统运行数据 使用工具&#xff1a;Live-F…...

Oracle EBS R12.2 汉化

一、前言 在使用oracle ebs时&#xff0c;使用中文会更好的理解整个ebs流程&#xff0c;以下介绍oracle r12中文补丁的方式 如果你的系统除了支持英语外&#xff0c;还支持其他语言&#xff0c;比如中文&#xff0c;那你在下载补丁的时候除了下载Generic Platform版本外&#…...

【Java面试笔记:基础】12.Java有几种文件拷贝方式?哪一种最高效?

在 Java 中,文件拷贝可以通过多种方式实现,不同方式的性能和适用场景有所差异。 1. Java 文件拷贝方式 传统 IO 方式 使用 FileInputStream 和 FileOutputStream,通过循环读取和写入数据实现文件拷贝。 示例代码: try (InputStream is = new FileInputStream("sou…...

互联网大厂Java面试:RocketMQ、RabbitMQ与Kafka的深度解析

互联网大厂Java面试&#xff1a;RocketMQ、RabbitMQ与Kafka的深度解析 面试场景 面试官&#xff1a;马架构&#xff0c;您好&#xff01;欢迎参加我们的面试。今天我们将围绕消息中间件展开讨论&#xff0c;尤其是RocketMQ、RabbitMQ和Kafka。您有十年的Java研发和架构设计经…...

kali安装切换jdk1.8.0_451java8详细教程

kali安装切换jdk1.8.0_451java8详细教程 下载链接&#xff1a; jdk-8u451-linux-i586.tar.gz 链接: https://pan.baidu.com/s/1lpgI0JMfHpZ__RxsF8UoBw?pwdx3z2 提取码: x3z2 解压jdk 首先将下载好的压缩包放在kali虚拟机中&#xff0c;一般是直接拖到桌面 然后cd到压缩包…...

众趣科技X世界读书日丨数字孪生技术赋能图书馆空间智慧化运营

4月23日&#xff0c;是第30个“世界读书日”&#xff0c;不仅是庆祝阅读的日子&#xff0c;更是思考知识传播未来的契机。 图书馆作为主要传播图书的场所&#xff0c;在科技的发展中&#xff0c;图书馆正面临前所未有的挑战&#xff0c;联合国数据显示&#xff0c;全球近30%的…...

Python内置函数-aiter()

Python内置函数 aiter() 用于获取异步可迭代对象的异步迭代器&#xff0c;是异步编程中的核心工具之一。 1. 基本概念 异步可迭代对象&#xff1a;实现了 __aiter__() 和 __anext__() 方法的对象&#xff0c;支持 async for 循环。 异步迭代器&#xff1a;通过 aiter() 获取的…...

Java 实现单链表翻转(附详细注释)

1. 引言 单链表&#xff08;Singly Linked List&#xff09;是一种常见的数据结构&#xff0c;在算法和数据结构的学习中占有重要地位。翻转单链表是一道经典的面试题&#xff0c;本文将介绍几种常见的 Java 实现方法&#xff0c;并详细讲解关键步骤的含义。 2. 单链表定义 …...

基于HPC的气候模拟GPU加速实践全流程解析

基于HPC的气候模拟GPU加速实践全流程解析 关键词&#xff1a;气候模型、GPU加速、CUDA编程、性能优化、分布式训练 摘要&#xff1a; 本文针对全球气候模拟中10^12级网格点实时计算需求&#xff0c;提出基于CUDA的并行计算架构。通过改进WRF模式的分块矩阵乘法算法&#xff0c…...

【初级】前端开发工程师面试100题(一)

本题库共计包含100题,考察html,css,js,以及react,vue,webpack等基础知识掌握情况。 HTML基础篇 说说你对HTML语义化的理解? 语义化就是用合适的标签表达合适的内容,比如<header>表示页眉,<nav>表示导航。这样不仅代码更清晰,对SEO也友好,屏幕阅读器也能…...

大模型框架技术演进与全栈实践指南

‌一、大模型框架概述 ‌大模型框架‌是支撑大规模语言模型&#xff08;LLM&#xff09;训练、推理和应用开发的核心技术体系&#xff0c;涵盖分布式训练、高效推理、应用编排等全流程。从AlphaGo到GPT-4&#xff0c;大模型框架的进化推动AI从实验室走向工业化落地。据IDC预测…...

【Bug】 [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed

当你在进行深度学习相关操作时&#xff0c;若因缺少本地的 CA 证书而无法下载资源&#xff0c;下面为你介绍几种解决办法&#xff1a; 方法一&#xff1a;更新 CA 证书 在大多数 Linux 发行版中&#xff0c;你可以使用包管理器来更新 CA 证书。例如&#xff0c;在基于 Debian…...

第七章:Workspace Security

Chapter 7: Workspace Security 从变形金刚到安全防护罩&#xff1a;如何为代理设置权限边界&#xff1f; 在上一章多后端配置&#xff0c;我们学会了让代理像变形金刚一样切换不同环境。但就像超级英雄需要遵守法律一样&#xff0c;代理也需要一个“安全防护罩”来限制它的操…...

【论文阅读】Hierarchical Group-Level Emotion Recognition

【论文阅读】Hierarchical Group-Level Emotion Recognition 摘要1.介绍2.相关工作3.方法4.实验5.分析 摘要 本篇博客参考IEEE于2021年收录的论文Hierarchical Group-Level Emotion Recognition&#xff0c;对其主要内容进行总结&#xff0c;以便加深理解和记忆 1.介绍 1&am…...

(2025最新版)CUDA安装及环境配置

CUDA安装 文章目录 CUDA安装检查本地环境下载CUDA安装包CUDA安装检查是否安装成功 学习深度学习的小伙伴在配置环境的时候必不可少的一件事就是安装CUDA&#xff0c;在这个过程中也是容易踩很多坑&#xff0c;所以这里写一篇教程来帮助新入门的小伙伴快速安装CUDA&#xff0c;减…...

ODC 4.3.4 发布:三大核心功能升级,打造更好的数据开发体验

ODC 是OceanBase提供的企业级数据库协同开发平台&#xff0c;提供了团队协作开发的基础框架&#xff0c;和14种工单任务类型。此次升级的 ODC 4.3.4版本&#xff0c;重点优化了30余项功能&#xff0c;主要聚焦快速上手、配置管理和核心功能中的改进&#xff0c;来为用户打造更高…...

JavaFX 第一篇 Hello World

1、简介 JavaFX 是一个用于构建客户端应用程序的 Java 库&#xff0c;作为 Java 标准库的一部分&#xff08;JDK 8 到 10&#xff09;&#xff0c;从 JDK 11 开始&#xff0c;JavaFX 将以独立模块发布&#xff0c;将不再包含在 JDK标准库中&#xff0c;他是 Java 应用程序开发的…...

es的range失效

es的range失效的解决方法 问题描述 当我们es使用keyword类型存储数字时&#xff0c;当我们使用range时我们发现range失效的问题&#xff0c;例如以下的用例&#xff1a; 我们创建一个test1的索引test1&#xff1a; 使用_bulk进行批量导入数据&#xff1a; 进行查询我们发现我…...

gem5-gpu教程03 当前的gem5-gpu软件架构(因为涉及太多专业名词不知道该如何翻译所以没有汉化)

Current gem5-gpu Software Architecture 这是当前gem5-gpu软件架构的示意图。 CudaCore (src/gpu/gpgpu-sim/cuda_core.*, src/gpu/gpgpu-sim/CudaCore.py) Wrapper for GPGPU-Sim shader_core_ctx (gpgpu-sim/gpgpu-sim/shader.h) Sends instruction, global and const m…...

【C++】vector扩容缩容

vector扩容缩容 1 扩容 一般来说&#xff0c;主要是重新分配内存 2 缩容 resize 缩小后&#xff0c;vector 的容量&#xff08;capacity()&#xff09;可能保持不变&#xff0c;需要显式调用 shrink_to_fit() 来释放内存。 验证代码&#xff1a; #include <vector>…...

【鸿蒙HarmonyOS】深入理解router与Navigation

5. 路由 1.页面路由(router模式&#xff09; 1.概述 页面路由指的是在应用程序中实现不同页面之间的跳转&#xff0c;以及数据传递。 我们先明确自定义组件和页面的关系&#xff1a; 自定义组件&#xff1a;Component 装饰的UI单元&#xff0c;页面&#xff1a;即应用的UI…...

手机端touch实现DOM拖拉功能

touch实现DOM拖拉功能 1、面板交互流程图 [ 用户触摸拖动手柄 ]↓ [ 记录起始位置、偏移量 ]↓ [ 实时更新面板 translateY ]↓ [ 手指松开 → 判断释放位置 ]↓ [ 达到恢复条件&#xff1f; → 复位 ]2、详细实现步骤 2.1 初始面板位置 const initialPosition () > tr…...

Discuz!与DeepSeek的AI融合:打造智能网址导航新体验——以“虎跃办公”为例

在数字化办公需求日益增长的今天&#xff0c;高效获取优质资源成为职场人士的核心痛点。传统网址导航网站往往面临信息过载、个性化不足、交互体验单一等问题&#xff0c;难以满足用户精准触达目标资源的需求。本文将深入剖析“虎跃办公”这一基于Discuz!系统构建的网址导航网站…...

【AI】Windows环境安装SPAR3D单图三维重建心得

效果一览 左图为原始单个图像&#xff0c;右图为通过SPAR3D重建后的三维建模&#xff0c;可以看出效果还是不错的。 本地环境配置 系统&#xff1a;Windows 11 专业版CPU&#xff1a;i5-13400F内存&#xff1a;32GBGPU&#xff1a;RTX3060 12GBcuda&#xff1a;11.8conda&…...

关于Agent的简单构建和分享

前言&#xff1a;Agent 具备自主性、环境感知能力和决策执行能力&#xff0c;能够根据环境的变化自动调整行为&#xff0c;以实现特定的目标。 一、Agent 的原理 Agent(智能体)被提出时&#xff0c;具有四大能力 感知、分析、决策和执行。是一种能够在特定环境中自主行动、感…...

【C/S通信仿真】

文章目录 一、实验背景与目的二、实验设计与实现思路1. 设计思想2. 核心代码实现 总结 一、实验背景与目的 在网络编程中&#xff0c;TCP 协议是实现可靠通信的核心。本次实验基于 Windows 平台&#xff0c;使用 WinSock2 库实现客户端与服务器的双向数据传递&#xff0c;模拟…...

Tomcat 8 启动闪退解决方案:版本差异与调试技巧详解

在使用 Tomcat 8 时&#xff0c;启动闪退是常见问题&#xff0c;核心原因多与 JAVA_HOME 环境变量配置、版本特性及启动脚本逻辑相关。本文结合官方文档与专家实践&#xff0c;提供分版本解决方案及调试技巧&#xff0c;适用于开发与运维场景。 一、核心问题&#xff1a;JAVA_…...

【Project】基于spark-App端口懂车帝数据采集与可视化

文章目录 hadoop完全分布式部署hdfs-site.xmlcore-site.xmlmarpred-site.xmlyarn-site.xml spark集群部署spark-env.sh mongodb分片模式部署config 服务器初始化config 副本集 shard 服务器初始化shard 副本集 mongos服务器添加shard设置chunk大小 启动分片为集合 user 创建索引…...

基于ARM+FPGA+DSP的储能协调控制器解决方案,支持国产化

储能协调控制器的作用与设计方案 一、‌核心作用‌ ‌实时监测与协调控制‌ 实时采集储能系统电压、电流、温度等参数&#xff0c;监测电池电量状态及充放电功率&#xff0c;动态调整储能与电网、负载的功率交互&#xff0c;保障能源供需平衡15。支持一次调频&#xff08;AGC&a…...

将天气查询API封装为MCP服务

下面我将展示如何将一个天气查询API封装为符合MCP协议的服务。我们将使用Python实现&#xff0c;包括服务端和客户端。 ## 1. 服务端实现 python # weather_mcp_server.py from fastapi import FastAPI, HTTPException from pydantic import BaseModel from typing import Di…...

JSON实现动态按钮管理的Python应用

在开发桌面应用程序时&#xff0c;动态生成用户界面元素并根据配置文件灵活管理是一项常见需求。本文将介绍如何使用Python的wxPython库结合JSON配置文件&#xff0c;开发一个支持动态按钮创建、文件执行和配置管理的桌面应用程序。该应用允许用户通过设置界面配置按钮名称和关…...

基于GA遗传优化TCN-BiGRU注意力机制网络模型的时间序列预测算法matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 (完整程序运行后无水印) 2.算法运行软件版本 matlab2024b&#xff08;提供软件版本下载&#xff09; 3.部分核心程序 &#xff08;完整版代码包…...

MongoDB(docker版)备份还原

docker启动MongoDB docker run -d -p 27017:27017 --name my-mongo -v /mongodb/db:/data/db mongo备份MongoDB 使用mongodump备份数据库时&#xff0c;默认会将备份数据保存在当前工作目录下的dump文件夹中。 docker容器中默认备份在当前工作目录&#xff0c;所以此处指定当…...

[蓝桥杯 2025 省 Python B] 异或和

暴力&#xff08;O(n^2)&#xff09;&#xff1a; def xor_sum(n, arr):total 0for i in range(n):for j in range(i 1, n):total (arr[i] ^ arr[j]) * (j - i)return total# 主函数 if __name__ "__main__":n int(input())arr list(map(int, input().split()…...

HTTP代理基础:网络新手的入门指南

目录 一、为什么需要了解HTTP代理&#xff1f; 二、HTTP代理的“中间人”角色 三、代理的三大核心类型 四、HTTP代理的5大实用场景 五、设置代理的三种方式 六、代理的优缺点分析 七、如何选择代理服务&#xff1f; 八、安全使用指南 九、未来趋势 结语 一、为什么需要…...