别人没那么重要,我也没那么重要,好好活着,把能做的小事做好,够不到的东西就放弃,承认就好。做一个心情好能睡着的人,你所有事情都会在正轨上。

【C++】标准模板库STL-算法

  C++标准模板库(STL)中的算法是一套通用的、类型安全的函数模板,它们可以在不同的容器上工作。算法使用迭代器来访问容器中的元素,因此它们与容器的具体类型无关。这使得STL算法非常灵活和强大。以下是一些常用的C++ STL算法:

1. 非修改算法

  在C++ STL中,非修改算法是那些不改变容器中元素值的算法。这些算法主要用于搜索、查找、计数和应用函数等操作,而不改变原有数据。以下是一些常用的非修改算法:

1.1. std::for_each

  对区间内的每个元素执行给定的操作。

1 std::vector<int> v = {1, 2, 3};
2 std::for_each(v.begin(), v.end(), [](int i) { std::cout << i << ' '; });

1.2. std::findstd::find_if

  std::find 查找给定值的第一个出现。

1 auto it = std::find(v.begin(), v.end(), 2); // 找到值为2的元素

  std::find_if 查找满足特定条件的第一个元素。

1 auto it = std::find_if(v.begin(), v.end(), [](int i) { return i > 1; }); // 找到第一个大于1的元素

1.3. std::countstd::count_if

  std::count 计算区间内等于某个值的元素数量。

1 size_t count = std::count(v.begin(), v.end(), 2); // 计算值为2的元素数量

  std::count_if 计算满足某个条件的元素数量。

1 size_t count = std::count_if(v.begin(), v.end(), [](int i) { return i > 1; }); // 计算大于1的元素数量

1.4. std::mismatch

  找出两个区间中第一个不匹配的元素。

1 std::vector<int> v2 = {2, 3, 4};
2 auto result = std::mismatch(v.begin(), v.end(), v2.begin());

1.5. std::equal

  比较两个区间是否相等。

1 std::vector<int> v2 = {1, 2, 3};
2 bool areEqual = std::equal(v.begin(), v.end(), v2.begin());

1.6. std::searchstd::search_n

  std::search 在一个区间中搜索另一个区间的首次出现。

1 std::vector<int> pattern = {2, 3};
2 auto it = std::search(v.begin(), v.end(), pattern.begin(), pattern.end());

  std::search_n 在一个区间中搜索特定值的连续出现。

1 auto it = std::search_n(v.begin(), v.end(), 2, 1); // 查找2个连续的1

1.7. std::binary_search

  检查一个值是否存在于有序区间内。

1 std::sort(v.begin(), v.end());
2 bool found = std::binary_search(v.begin(), v.end(), 2);

1.8. std::lower_boundstd::upper_bound

  std::lower_bound 查找有序区间内第一个不小于给定值的元素。

1 auto it = std::lower_bound(v.begin(), v.end(), 2);

  std::upper_bound 查找有序区间内第一个大于给定值的元素。

1 auto it = std::upper_bound(v.begin(), v.end(), 1);

1.9. 注意

  • 非修改算法不会改变容器中的元素值,但可能会改变容器的迭代器、指针或引用的有效性。
  • 某些算法,如 std::sortstd::stable_sort,虽然可以归类为非修改算法,因为它们不改变元素的值,但它们改变了元素的顺序,所以使用时需要注意。
  • 算法的执行可能依赖于比较函数或谓词,这允许算法以灵活的方式使用。

2. 修改算法

  C++ STL中的修改算法会改变容器中元素的值或顺序。以下是一些常用的修改算法:

2.1. std::fill

  用指定的值填充某个区间。

1 std::vector<int> v(5);
2 std::fill(v.begin(), v.end(), 1); // 将v中的所有元素设置为1

2.2. std::fill_n

  用指定的值填充区间中的前n个元素。

1 std::fill_n(v.begin(), 3, 2); // 将v的前三个元素设置为2

2.3. std::transform

  应用给定的函数到区间中的每个元素上,并将结果存储到另一个区间。

1 std::vector<int> v(5, 1), w(5, 0);
2 std::transform(v.begin(), v.end(), w.begin(), [](int i) { return i * 2; }); // 将v中的每个元素乘以2,存入w

2.4. std::replace

  将区间中满足特定值的元素替换为另一个值。

1 std::replace(v.begin(), v.end(), 1, 3); // 将v中所有1替换为3

2.5. std::replace_if

  将区间中满足特定条件的元素替换为另一个值。

1 std::replace_if(v.begin(), v.end(), [](int i) { return i < 3; }, 4); // 将v中所有小于3的元素替换为4

2.6. std::generate

  为区间中的每个元素生成新的随机值。

1 std::generate(v.begin(), v.end(), rand); // 用随机数填充v

2.7. std::removestd::remove_if

  从区间中移除满足特定值或条件的元素,但不实际删除它们,只是将它们移动到区间的末尾。

1 auto new_end = std::remove(v.begin(), v.end(), 3); // 将v中所有3移动到末尾
2 v.erase(new_end, v.end());

2.8. std::unique

  移除排序区间中所有连续重复的元素,并将剩余元素移动到区间的开始处。

1 auto new_end = std::unique(v.begin(), v.end()); // 移除连续重复的元素
2 v.erase(new_end, v.end());

2.9. std::rotate

  向右旋转区间中的元素。

1 std::vector<int> v = {1, 2, 3, 4, 5};
2 std::rotate(v.begin(), v.begin() + 2, v.end()); // 将v旋转,使得原v[2]成为第一个元素

2.10. std::random_shuffle

  随机打乱区间中的元素。

1 std::random_shuffle(v.begin(), v.end()); // 随机打乱v中的元素

2.11. 注意

  • 修改算法改变了容器中的元素,但不会改变容器的元素数量。
  • 某些算法,如 std::uniquestd::remove_if,会将不需要的元素移动到区间的末尾,然后需要用 erase 方法来真正地从容器中移除这些元素。
  • 算法的执行可能依赖于谓词函数,这允许算法以灵活的方式使用。
  • 修改算法是STL中用于数据变换和操作的重要工具,它们可以用于数据预处理、数据清洗和其他需要修改数据集合的场景。

3. 排序算法

  C++ STL提供了几个排序算法,它们对容器中的元素执行全排序或部分排序操作。以下是一些常用的排序算法:

3.1. std::sort

  对给定区间内的元素执行全排序。

1 std::vector<int> v = {5, 1, 4, 2, 8};
2 std::sort(v.begin(), v.end()); // 将v中的元素升序排序

3.2. std::stable_sort

  与std::sort类似,但保持相等元素的原始顺序,即稳定排序。

1 std::stable_sort(v.begin(), v.end()); // 稳定排序v中的元素

3.3. std::partial_sort

  部分排序,确保区间[first, middle)中的元素是整个区间[first, last)中最小的元素,并按排序准则排序。

1 std::partial_sort(v.begin(), v.begin() + 2, v.end()); // 部分排序v的前两个元素

3.4. std::nth_element

  确保第n个位置的元素放置在排序后它应该在的位置,并保证其之前的所有元素都不大于它,之后的所有元素都不小于它。

1 std::nth_element(v.begin(), v.begin() + 2, v.end()); // 将v中的第3个位置的元素放到正确的位置

3.5. std::sort_heap

  将一个最大堆重新排序为升序序列。

1 std::priority_queue<int> pq; // 默认为最大堆
2 while (!pq.empty()) {
3     pq.pop();
4 }
5 std::sort_heap(pq.begin(), pq.end()); // 将pq中的元素排序

3.6. 注意

  • std::sort是最常用的排序算法,它使用快速排序、堆排序、插入排序的混合算法,提供很好的平均性能。
  • std::stable_sortstd::sort 慢,但它保持相等元素的相对顺序,适用于需要稳定性的场景。
  • std::partial_sortstd::nth_element 比全排序算法快,因为它们不需要对整个区间进行排序。
  • 排序算法通常要求容器中的元素类型支持小于操作符(<),或者你可以提供一个自定义的比较函数。
  • 排序算法是C++ STL中非常重要的一部分,它们广泛应用于各种需要排序的场景,如数据处理、算法实现等。正确选择和使用排序算法对于提高程序性能和代码效率至关重要。

4. 二分查找算法

  在C++ STL中,二分查找算法是在有序区间中进行搜索的有效方法。为了使用二分查找算法,目标区间必须是有序的。以下是几个常用的二分查找相关的函数:

4.1. std::lower_bound

  返回指向不小于(等于或大于)给定值的第一个元素的迭代器。

1 std::vector<int> v = {1, 2, 4, 4, 5, 7, 7};
2 std::sort(v.begin(), v.end()); // 先对v进行排序
3 
4 auto it = std::lower_bound(v.begin(), v.end(), 4);
5 if (it != v.end()) {
6     std::cout << "The lower bound of 4 is: " << *it << '\n';
7 }

4.2. std::upper_bound

  返回指向大于给定值的第一个元素的迭代器。

1 auto it = std::upper_bound(v.begin(), v.end(), 4);
2 if (it != v.end()) {
3     std::cout << "The upper bound of 4 is: " << *it << '\n';
4 }

4.3. std::binary_search

  检查一个值是否存在于有序区间内,如果存在,返回true

1 bool found = std::binary_search(v.begin(), v.end(), 4);
2 if (found) {
3     std::cout << "The value 4 is present in the vector.\n";
4 }

4.4. std::equal_range

  返回一个包含两个元素的std::pair,分别表示lower_boundupper_bound的结果。

1 auto range = std::equal_range(v.begin(), v.end(), 4);
2 for (auto it = range.first; it != range.second; ++it) {
3     std::cout << "Found 4 at index: " << std::distance(v.begin(), it) << '\n';
4 }

4.5. 注意

  • 所有这些函数都要求目标区间是有序的。对于std::lower_boundstd::upper_bound,区间可以是任意有序的(不一定要是非降序或升序)。
  • std::binary_search实际上是使用std::lower_boundstd::upper_bound来工作的,它检查lower_boundupper_bound返回的迭代器是否相同,如果相同,表示没有重复值,并且找到了目标值。
  • std::equal_range可以用于找到与给定值相等的所有元素的边界,这在多重集合或包含重复元素的有序集合中非常有用。
  • 二分查找算法是C++ STL中查找操作的重要工具,特别是在处理大型数据集时,它们的效率远高于线性搜索。

5. 集合算法

  C++ STL提供了一组集合算法,这些算法用于处理集合容器,如 std::setstd::unordered_setstd::multisetstd::unordered_multiset。以下是一些常用的集合算法:

5.1. std::includes

  检查一个序列是否完全包含在另一个序列中。

1 std::vector<int> v1 = {1, 2, 3};
2 std::vector<int> v2 = {2, 3, 4};
3 bool result = std::includes(v1.begin(), v1.end(), v2.begin(), v2.end());

5.2. std::set_union

  创建两个集合的并集。

1 std::vector<int> v1 = {1, 2, 3};
2 std::vector<int> v2 = {3, 4, 5};
3 std::vector<int> unionVec;
4 std::set_union(v1.begin(), v1.end(), v2.begin(), v2.end(), std::back_inserter(unionVec));

5.3. std::set_intersection

  创建两个集合的交集。

1 std::vector<int> intersectionVec;
2 std::set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(), std::back_inserter(intersectionVec));

5.4. std::set_difference

  创建两个集合的差集,即存在于第一个集合但不在第二个集合中的元素。

1 std::vector<int> differenceVec;
2 std::set_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), std::back_inserter(differenceVec));

5.5. std::set_symmetric_difference

  创建两个集合的对称差集,即只存在于一个集合而不在两个集合都存在的元素。

1 std::vector<int> symmetricDifferenceVec;
2 std::set_symmetric_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), std::back_inserter(symmetricDifferenceVec));

5.6. 注意

  • 集合算法要求输入的集合是有序的,对于 std::setstd::multiset 这是自动满足的,但对于 std::vector 或其他序列容器,需要先进行排序或使用 std::includes 来确保有序。
  • 集合算法通常用于处理集合类型的容器,但也可以与适配了集合语义的容器一起使用,如通过 std::back_inserter 或其他插入迭代器适配的 std::vector
  • 集合算法执行的操作是集合论中的典型操作,它们在处理集合数据结构时非常有用,例如在数据库操作、搜索算法和图算法中。

6. 计数和搜索算法

  C++ STL提供了多种计数和搜索算法,这些算法用于在容器中查找特定元素或统计元素的出现次数。以下是一些常用的计数和搜索算法:

6.1. std::find

  在容器中查找特定值的第一个出现。

1 std::vector<int> v = {1, 2, 3, 4, 5};
2 auto it = std::find(v.begin(), v.end(), 3);
3 if (it != v.end()) {
4     // 找到了值3
5 }

6.2. std::find_if

  在容器中查找满足特定条件的第一个元素。

1 auto it = std::find_if(v.begin(), v.end(), [](int i) { return i > 3; });
2 if (it != v.end()) {
3     // 找到了第一个大于3的元素
4 }

6.3. std::count

  计算容器中等于某个特定值的元素数量。

1 size_t count = std::count(v.begin(), v.end(), 3);

6.4. std::count_if

  计算容器中满足某个条件的元素数量。

1 size_t count = std::count_if(v.begin(), v.end(), [](int i) { return i > 3; });

6.5. std::distance

  计算两个迭代器之间的距离,即元素的数量。

1 auto dist = std::distance(v.begin(), it); // 计算从v.begin()到it之间的元素数量

6.6. std::search

  在一个大容器中搜索一个小容器的首次出现。

1 std::vector<int> v2 = {3, 4, 5};
2 auto it = std::search(v.begin(), v.end(), v2.begin(), v2.end());
3 if (it != v.end()) {
4     // 在v中找到了v2
5 }

6.7. std::search_n

  在容器中搜索特定值的连续出现。

1 auto it = std::search_n(v.begin(), v.end(), 3, 2);
2 if (it != v.end()) {
3     // 在v中找到了连续的2个3
4 }

6.8. std::mismatch

  找出两个区间中对应位置上第一个不匹配的元素。

1 std::vector<int> v2 = {1, 2, 3, 4, 5};
2 auto it = std::mismatch(v.begin(), v.end(), v2.begin());
3 if (it.first != v.end()) {
4     // v和v2在it.first处不匹配
5 }

6.9. 注意

  • 计数和搜索算法不修改容器中的元素,即属于不修改算法
  • 算法通常接受一对迭代器作为区间的定义,迭代器类型应该与容器的迭代器类型相匹配。
  • std::findstd::count 通常用于查找特定值,而 std::find_ifstd::count_if 更灵活,允许使用自定义条件。
  • std::searchstd::search_n 可以在一个区间内搜索另一个区间或特定值的连续出现。

7. 复制算法

  C++ STL中的复制算法允许你复制容器中的元素到另一个容器或区间,同时可以选择性地修改元素或应用某种条件。以下是一些常用的复制算法:

7.1. std::copy

  将一个区间的元素复制到另一个区间。

1 std::vector<int> src = {1, 2, 3};
2 std::vector<int> dest(src.size());
3 std::copy(src.begin(), src.end(), dest.begin());

7.2. std::copy_backward

  与std::copy相似,但方向相反,从区间的末尾向前复制,这样可以在就地操作时避免覆盖目标区间的前部分。

1 std::copy_backward(src.begin(), src.end(), dest.end());

7.3. std::copy_n

  复制区间中的前n个元素。

1 std::vector<int> dest2(src.size());
2 std::copy_n(src.begin(), 2, dest2.begin()); // 只复制前两个元素

7.4. std::copy_if

  复制满足特定条件的元素。

1 std::vector<int> dest3(src.size());
2 std::copy_if(src.begin(), src.end(), dest3.begin(), [](int i) { return i > 1; }); // 只复制大于1的元素

7.5. std::move

  复制(移动)一个区间的元素到另一个区间,对于某些资源分配的类型(如动态数组),使用std::move可以避免不必要的复制。

1 std::vector<int> src2 = {4, 5, 6};
2 std::vector<int> dest4(src2.size());
3 std::move(src2.begin(), src2.end(), dest4.begin());

7.6. 注意

  • 复制算法不会修改源区间的元素,但可能会修改目标区间的元素。
  • std::copystd::copy_backward 之间的主要区别在于复制的方向,std::copy_backward 适用于目标区间与源区间有重叠的情况。
  • std::copy_if 允许你使用一个谓词来选择性地复制元素。
  • std::move 是一个特例,它不是复制算法,而是移动算法,它用于将资源从源区间“移动”到目标区间,而不是复制。这在源区间不再需要时非常有用,因为它可以避免不必要的复制开销。
  • 复制算法是C++ STL中用于数据转移的重要工具,它们在数据迁移、容器转换和算法实现中非常有用。正确选择和使用复制算法对于提高程序性能和代码效率至关重要。

8. 替换算法

  C++ STL 提供了几个替换算法,用于修改容器中的元素,将满足特定条件的元素替换为另一个值或另一个对象。以下是一些常用的替换算法:

8.1. std::replace

  将区间中所有等于某个指定值的元素替换为另一个值。

1 std::vector<int> v = {1, 2, 3, 2, 1};
2 std::replace(v.begin(), v.end(), 2, 4); // 将所有2替换为4

8.2. std::replace_if

  将区间中所有满足某个条件的元素替换为一个值。

1 std::replace_if(v.begin(), v.end(), [](int i) { return i < 3; }, 0); // 将所有小于3的元素替换为0

8.3. std::replace_copy

  执行 std::replace 的操作,并将结果复制到一个新区间。

1 std::vector<int> v = {1, 2, 3, 2, 1};
2 std::vector<int> v2(v.size());
3 std::replace_copy(v.begin(), v.end(), v2.begin(), 2, 4); // 将v中的2替换为4,并复制到v2

8.4. std::replace_copy_if

  执行 std::replace_if 的操作,并将结果复制到一个新区间。

1 std::vector<int> v2(v.size());
2 std::replace_copy_if(v.begin(), v.end(), v2.begin(), [](int i) { return i < 3; }, 0); // 将v中小于3的元素替换为0,并复制到v2

8.5. 注意

  • 替换算法不改变原始容器中的元素数量,只是修改了元素的值。
  • std::replacestd::replace_if直接在原始容器上操作,而 std::replace_copystd::replace_copy_if 则将修改后的结果复制到另一个容器。
  • 使用 std::replace_ifstd::replace_copy_if允许你通过谓词函数灵活地定义替换条件。
  • 替换算法通常用于数据清洗、数据预处理或在算法实现中根据需要修改元素值。
  • 替换算法是C++ STL中用于数据变换的重要工具,它们提供了一种高效且类型安全的方式来更新容器中的元素。

9. 总结

  除了以上算法,还有很多,例如排列算法、堆算法、流算法……在此就不一一列举,有需要用到的可以参考官网手册。

 

时间:2024年5月1日

 

posted @ 2024-05-01 11:41  一路狂奔的乌龟  阅读(117)  评论(0)    收藏  举报
返回顶部