【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::find 和 std::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::count 和 std::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::search 和 std::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_bound 和 std::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::sort和std::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::remove 和 std::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::unique和std::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_sort比std::sort慢,但它保持相等元素的相对顺序,适用于需要稳定性的场景。std::partial_sort和std::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_bound和upper_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_bound和std::upper_bound,区间可以是任意有序的(不一定要是非降序或升序)。 - s
td::binary_search实际上是使用std::lower_bound和std::upper_bound来工作的,它检查lower_bound和upper_bound返回的迭代器是否相同,如果相同,表示没有重复值,并且找到了目标值。 std::equal_range可以用于找到与给定值相等的所有元素的边界,这在多重集合或包含重复元素的有序集合中非常有用。- 二分查找算法是C++ STL中查找操作的重要工具,特别是在处理大型数据集时,它们的效率远高于线性搜索。
 
5. 集合算法
  C++ STL提供了一组集合算法,这些算法用于处理集合容器,如 std::set、std::unordered_set、std::multiset 和 std::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::set和std::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::find和std::count通常用于查找特定值,而std::find_if和std::count_if更灵活,允许使用自定义条件。std::search和std::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::copy和std::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::replace和std::replace_if直接在原始容器上操作,而std::replace_copy和std::replace_copy_if则将修改后的结果复制到另一个容器。- 使用 
std::replace_if和std::replace_copy_if允许你通过谓词函数灵活地定义替换条件。 - 替换算法通常用于数据清洗、数据预处理或在算法实现中根据需要修改元素值。
 - 替换算法是C++ STL中用于数据变换的重要工具,它们提供了一种高效且类型安全的方式来更新容器中的元素。
 
9. 总结
除了以上算法,还有很多,例如排列算法、堆算法、流算法……在此就不一一列举,有需要用到的可以参考官网手册。
时间:2024年5月1日

        【C++】标准模板库STL-算法
    
                
            
        
浙公网安备 33010602011771号