C++容器算法示例详解

 更新时间:2024年08月28日 08:55:52   作者:solicit  
在谈到容器算法,我们大概率会用到谓词predicate,谓词返回的类型是布尔类型(bool)可以是lambda表达式、函数对象以及其它可调用的对象,这篇文章主要介绍了C++容器算法,需要的朋友可以参考下

容器算法

<algorithm>c++自带的容器算法,提供一系列实用的算法。在谈到容器算法,我们大概率会用到谓词predicate,谓词返回的类型是布尔类型(bool)可以是lambda表达式、函数对象以及其它可调用的对象。

查找

find()查找元素

find接受三个参数,第三个参数是值类型,setmap自带count函数也能实现这样的功能,返回值0表示不存在。为了方便本次连带find_iffind_if_notfind_first_offind_endadjacent_find一起举例。

vector<int> a{1, 1, 2, 3, 4, 4, 5};
vector<int> b{5, 6, 6};
find(a.begin(), a.end(), 3); // 返回的是迭代器,未查找到返回a.end()
find_if(a.begin(), a.end(), [](auto x){return x != 3;}); //接受一个一元谓词,找到第一个满足条件的元素
find_if_not(a.begin(), a.end(), [](auto x){return x != 3;}); //接受一个一元谓词,找到第一个不满足条件的元素
// find_first_of找到存在于第一个范围的第二个范围中的第一个元素(可能并不是第二个范围第一个元素)返回迭代器,也支持一个二元谓词。
vector<int> a{1, 1, 2, 3, 4, 4, 5};
vector<int> b{6, 5, 6};
auto it = find_first_of(a.begin(), a.end(), b.begin(), b.end());
cout << *it << endl; // 返回5
// find_end找到最后一个匹配子序列的位置,返回最后一个子序列开始的迭代器,也支持一个二元谓词
vector<int> a{1, 1, 2, 3, 1, 4, 1, 2};
vector<int> b{1, 2};
auto it = find_end(a.begin(), a.end(), b.begin(), b.end());
cout << distance(a.begin(), it) << endl; // 6指向第7个元素
// adjcent_find找到两个值邻近的元素,返回指向找到的第一个元素的迭代器,接受一个二元谓词
vector<int> a{1, 1, 2, 3, 4, 1, 2};
vector<int> b{1, 2};
auto it = adjacent_find(a.begin(), a.end());
auto it = adjacent_find(a.begin(), a.end(), 
    [](auto a, auto b)->bool{return  a + b = 10;});
cout << *it << endl;

去重

unique(nums.begin(), nums.end())除掉连续相同的值

unique函数的作用是删除掉的连续的相同的值,unique还支持传入二元谓词(可以理解为一个参数的函数),会返回一个迭代器it,但是并不是end(),中间是未指定的值(访问可能会产生未定义行为)。注意不要用setmap,返回的都是常量迭代器是无法改变的,即其指向的元素无法改变。

// 无谓词写法
vector<int> a{1, 1, 2, 3, 4, 4, 5};
auto it = unique(a.begin(), a.end()); // 此时a为{1, 2, 3, 4, 5}
for_each(a.begin(), a.end(), [](auto it){cout << it << endl;}); // 输出为{1, 2, 3, 4, 5, 4, 5}
for_each(a.begin(), it, [](auto it){cout << it << endl;}); // 输出为{1, 2, 3, 4, 5}
// 有二元谓词
vector<int> a{1, 1, 2, 3, 4, 4, 5};
auto it = unique(a.begin(), 
    a.end(), [](auto a, auto b) -> bool{return  a != b;}); // 此时a为{1, 2, 3, 4, 5},lambda表达式作为一个二元谓词,每次从数组中取两个严肃进行判断,然后删除不相等的元素。
for_each(a.begin(), a.end(), [](auto it) -> int{cout << it << endl;}); // Error,这是因为unique将返回的迭代器到之后的end()迭代器指向的值删除了,但是空间还在。此时访问,会发生未定义行为。
for_each(a.begin(), it, [](auto xs){cout << xs << endl;}); // 1,1
cout << distance(a.begin(), a.end()) << endl; // 7,distance求算距离是7

unique_copy()除掉连续相同的值并复制到目标容器

注意,使用此函数之前先分配空间,再使用unique_copy并不进行内存分配,只是赋值给另一个容器,如果访问未赋值部分的容器区域,那么会产生未定义行为。同样,也支持二元谓词,通过二元谓词判定移除连续值。

vector<int> a{1, 1, 2, 3, 4, 4, 5};
vector<int> b(7, 0);
auto it = unique_copy(a.begin(), a.end(), b.begin());
for_each(b.begin(), it, [](auto xs){cout << xs << endl;}); // out: 1 2 3 4 5
// 二元谓词, 一般不要使用二元谓词与数字直接进行比较,可以使用remove实现
auto it = unique_copy(a.begin(),
    a.end(), b.begin(), [x](auto a, auto b)
    -> bool{return a == 3;});
for_each(b.begin(), it, [](auto xs){cout << xs << endl;}); // out: 1 1 2 3,解释值等于3才会进行unique_copy
auto it = unique_copy(a.begin(),
    a.end(), b.begin(), [x](auto a, auto b)
    -> bool{return a == b;});
for_each(b.begin(), it, [](auto xs){cout << xs << endl;}); // out: 1 2 3 4 5,解释a = b,才会进行复制

排序

sort()对容器进行排序

sort用于实现容器元素的排序,sort也同样接受二元谓词

sort(a.begin(), a.end());
sort(a.begin(), a.end(), greater<>()); // 使用库中自带的greater\less对象

迭代器差值

distance()求迭代之间距离

distance用于求算两个迭代器之间的差值,只用于同一容器, setmapvector都可以用。

distance(c.begin(), c.end());

遍历容器

for_each()for_each_n遍历容器每个元素并执行函数规定的操作,第三个参数是一个函数。如果要对迭代器指向的元素修改,那么set就是不可以的,因为set返回的是常量迭代器。

array<int, 5> a{1, 2, 3, 4, 5};
for_each(a.begin(), a.end(), [](int& x){cout<< x; x *= 2;}); // 此时元素值会发生改变,因为捕获的是引用。
for_each(a.begin(), a.end(), [](auto x) -> void{x *= 2; cout << x;}); // 此时元素值不会发生改变,捕获的是值,会变为副本。
for_each_n(a.begin, 5, [](auto x) -> void{x *= 2; cout << x;}); // 第二个参数指定操作的数量n, 对n个元素进行操作。

复制元素

copy()能够将容器中的元素复制到输出迭代器之后out iterator

vector<vector<int>> sx(1);
// 建议复制前先分配多个空间,避免不必要的扩容
copy(sx.begin(), sx.end() , sx.begin()); // 可以将[begin, end)之间的元素复制到从begin()开始的位置。这种操作会将原先的元素覆盖一次
// 一个有意思的做法是使用back_inserter指明要进行尾插法,某些支持双向插入的容器也可以转变未尾插容器,省去了一些需要选择插入函数的麻烦。
// set、map类红黑树容器不存在后插方法,无法进行此类调用,注意
copy(sx.begin(), sx.end() , back_inserter(sx)); // 负值时会在末尾插入元素
vector<int> a{1, 2, 3};
set<int> s;
copy(a.begin(), a.end() , back_inserter(s)); // Error
// 接下来我们考虑一个向vector<vector<int>>复制的问题
vector<vector<int>> v(1); // 此时外部vector容量为1,size也是1。说明内部存在一个vector容器元素,但是此时vector[0]的size和capacity都是0,是一个空容器。
copy(v.begin(), v.end(), back_inserter(v)); // 此时内部空容器是2个

copy_backward()能够将容器中元素复制到输出迭代器之前。

map、set类也不支持。这个容器算法会覆盖掉输出迭代器之前的元素

vector<int> a{1, 2, 3, 4, 5, 6};
set<int> s;
copy_backward(a.begin(), a.end() , s); // Error
copy_backward(a.begin(), a.begin() + 3, a.end()); // OK a{1, 2, 3, 1, 2, 3}
copy_backward(a.begin(), a.begin() + 3, a.begin()); // OK,但是数组不会发生任何改变,相当于复制到begin()迭代器之前没有意义。

全排列

next_permutation提供返回容器的一个全排列,也支持一个二元谓词。

next_permutation(a.begin(), a.end());

取集合

set_intersection取交集、set_union取并集、set_difference取单侧差集,set_symmetric_difference取两侧差集,值得注意的是set_difference在输出到结果容器中只会输出第一个容器中的元素(不管第一个容器的元素是否少于第二个容器的元素)。这四个函数都接受一个二元谓词。注意:mapset都无法使用,因为这三个函数底层实现都需要用到自增运算符,而map和set只有常量迭代器不支持这样的操作。应用场景:vector等容器。

vector<int> a{1, 2, 2, 6, 7, 9};
vector<int> b{1, 2, 5, 8, 10};
vector<int> interResult;
vector<int> unionResult;
vector<int> differenceResult;
vector<int> symmetricResult;
set_intersection(a.begin(), a.end(),b.begin(), b.end(), back_inserter(interResult));
set_union(a.begin(), a.end(),b.begin(), b.end(), back_inserter(unionResult));
set_difference(a.begin(), a.end(),b.begin(), b.end(), back_inserter(differenceResult));
set_symmetric_difference(a.begin(), a.end(),b.begin(), b.end(), back_inserter(symmetricResult));
cout << "intersection:" <<endl;
for_each(interResult.begin(), interResult.end(), [](auto& item){cout << item << ":";});
cout << endl;
cout << "differencesection:" <<endl;
for_each(differenceResult.begin(), differenceResult.end(), [](auto& item){cout << item << ":";});
cout << endl;
cout << "unionsection:" <<endl;
for_each(unionResult.begin(), unionResult.end(), [](auto& item){cout << item << ":";});
cout << endl;
cout << "symmetric_diff_section:" <<endl;
for_each(symmetricDifferenceResult.begin(), symmetricDifferenceResult.end(), [](auto& item){cout << item << ":";});
/* output
* intersection:
* 1:2:
* differencesection:
* 2:6:7:9:
* unionsection:
* 1:2:2:5:6:7:8:9:10:
* symmetric_diff_section: 
* 2:6:7:9:5:8:10 */

搜索

search搜索串中的子序列,search_n搜索串中固定数量的连续重复值。search_n接受一个二元谓词,两个函数都能够使用setmapsearch接受一个searcher可调用对象,但是search_n对于set是没有意义的,因为set中不存在重复值

vector<int> a{1,1,1,4};
vector<int> b{1,2};
auto it = search(a.begin(), a.end(),b.begin(), b.end());
auto xs = search_n(a.begin(), a.end(), 3, 1);
int ret = it == a.end()? -1 : 1;
int res = xs == a.end()? -1 : 1;
cout << ret <<endl; // -1; 未找到
cout << res << endl; // 1; 找到了{1,1,1}

到此这篇关于C++容器算法的文章就介绍到这了,更多相关C++容器算法内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C语言中计算正弦的相关函数总结

    C语言中计算正弦的相关函数总结

    这篇文章主要介绍了C语言中计算正弦的相关函数总结,包括正弦和双曲线正弦以及反正弦的函数,需要的朋友可以参考下
    2015-08-08
  • C++ OpenCV实战之制作九宫格图像

    C++ OpenCV实战之制作九宫格图像

    本文将为大家介绍如何使用OpenCV C++ 制作九宫格图像,即将一张图像均等分成九份,然后将这九个小块按一定间隔拷贝到新画布上就可以啦。感兴趣的可以动手试一试
    2022-01-01
  • C/C++ 获取自身IP与域名片段的示例代码

    C/C++ 获取自身IP与域名片段的示例代码

    这篇文章主要介绍了C/C++ 获取自身IP与域名片段的示例代码,帮助大家更好的理解和学习C/C++编程,感兴趣的朋友可以了解下
    2020-10-10
  • C++虚函数表的原理与使用解析

    C++虚函数表的原理与使用解析

    对C++ 了解的人都应该知道虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的。简称为V-Table。本文就将详细讲讲虚函数表的原理与使用,需要的可以参考一下
    2022-04-04
  • C语言实现一个多线程委托模型的示例详解

    C语言实现一个多线程委托模型的示例详解

    这篇文章主要介绍了C语言实现一个多线程委托模型,这就是一个使用C语言实现多线程委托模型的例子,其中包含boss线程和worker线程,可以处理工作线程的异常情况,需要的朋友可以参考下
    2023-06-06
  • C++11中的引用限定符示例代码

    C++11中的引用限定符示例代码

    C++中有左值和右值的概念,其实,左值和右值的区分也同样适用于类对象,本文中将左值的类对象称为左值对象,将右值的类对象称为右值对象,对C++11 引用限定符相关知识感兴趣的朋友跟随小编一起看看吧
    2023-01-01
  • C++使用join拼接字符串的技巧

    C++使用join拼接字符串的技巧

    在C++中,经常需要将多个字符串拼接成一个大字符串,这个过程很容易出错,但有一些技巧可以帮助我们轻松地实现这个目标,五个C++中join字符串的技巧:使用stringstream、使用字符串迭代器、使用字符串的加法运算符、使用std::accumulate函数和使用boost库的join方法
    2023-11-11
  • C++string容器基本概念详解

    C++string容器基本概念详解

    c++相比c的一个好处就是实现了很多的容器和泛型算法,使得程序员的工作得到了很大的简化,本文重点给大家介绍C++string容器基本概念讲解,需要的朋友参考下吧
    2021-07-07
  • 详解C++编程中断言static_assert的使用

    详解C++编程中断言static_assert的使用

    这篇文章主要介绍了C++编程中断言static_assert的使用,断言在debug时非常有用,是C++入门学习中的基础知识,需要的朋友可以参考下
    2016-01-01
  • 深入详解C编写Windows服务程序的五个步骤

    深入详解C编写Windows服务程序的五个步骤

    本篇文章介绍了,使用C编写Windows服务程序的五个步骤的详细概述。需要的朋友参考下
    2013-05-05

最新评论