标准模板库(STL):标准库提供了一些可以直接使用的功能
指的是C++标准库中使用模板实现的内容(模板类和模板函数)
其中最主要的组件(容器/迭代器/算法)
容器:
可以存放其他对象的对象,就是容器,标准库中最典型的容器就是使用类模板实现的常用的数据结构根据数据结构本身的特点,可以把容器大概分为两类:
顺序容器:
如:数组、双端数组、栈、队列、链表 ......
关联容器:
如:集合(set),映射(map)
迭代器:
使用面向对象的技术封装起来的"高级指针",具有与指针类似的行为(表示一个位置,同时可以访问容器中这个位置的数据)
在标准库中,大部分的容器都实现了迭代器(用来访问容器中的元素),栈和队列没有
算法:使用函数模板实现的常用的操作容器的算法(功能),如:
排序,交换,查找,遍历,合并......
很多算法都是用来操作容器中的元素的(所有元素/使用迭代器表示的一个区间)
以方法为主,记住常用核心的接口和功能,其他的可以查看帮助文档——cppreference
01 迭代器(iterator)
迭代器用于一个对象集合上的元素进行遍历动作,对象集合可能是容器,也可能是容器的一部分
迭代器(iterator):是用面向对象的技术封装起来的"高级指针",表示集合中某一个元素的位置
目的是将遍历容器的方法和容器内部的实现分开,对于用户来说
不需要知道集合内部的结构,就可以使用一种通用的方法去遍历容器内部的所有元素(迭代器模式)迭代器是一个对象,相当于容器和操作容器的算法之间的中介(表示一个位置/范围)
迭代器可以指向容器中的某一个元素(位置),通过迭代器我们可以操作这个元素,所以迭代器和指针一样是类型
迭代器按照定义分为四类:1. 正向迭代器
定义方式:
容器类名<类型参数>::iterator 迭代器名称; // 实例化一个迭代器
array<int, 10> arr; arrar<int, 10>::iterator it = arr.begin(); // 定义并且初始化了一个迭代器对象 当++操作的时候,指向的位置是往后移动的 可以利用迭代器去读取和修改容器中的元素 容器类一般向外提供获取迭代器的接口: begin() 获取容器的正向迭代器的首部(第一个元素) end() 获取容器的正向迭代器的尾部(最后一个元素的后面)
2. 常量正向迭代器(const)
定义方式:
容器类名<类型参数>::const_iterator 迭代器名称; // 实例化一个迭代器
array<int, 10> arr; // 定义并且初始化了一个迭代器对象 arrar<int, 10>::const_iterator it = arr.cbegin(); 当++操作的时候,指向的位置是往后移动的 只能利用迭代器去读取元素,不能修改容器中元素的值 容器类一般向外提供获取迭代器的接口: cbegin() 获取容器的正向迭代器的首部(第一个元素) cend() 获取容器的正向迭代器的尾部(最后一个元素的后面)
3. 反向迭代器(reverse)
定义方式:
容器类名<类型参数>::reverse_iterator 迭代器名称; // 实例化一个迭代器
array<int, 10> arr; // 定义并且初始化了一个迭代器对象 arrar<int, 10>::reverse_iterator it = arr.rbegin(); 当++操作的时候,指向的位置是往前移动的 可以利用迭代器去读取和修改容器中的元素 容器类一般向外提供获取迭代器的接口: rbegin() 获取容器的反向迭代器的首部(最后一个元素) rend() 获取容器的反向迭代器的尾部(第一个元素的前面)
4. 常量反向迭代器
定义方式:
容器类名<类型参数>::const_reverse_iterator 迭代器名称; // 实例化一个迭代器
array<int, 10> arr; // 定义并且初始化了一个迭代器对象 arrar<int, 10>::const_reverse_iterator it = arr.crbegin(); 当++操作的时候,指向的位置是往前移动的 只能利用迭代器去读取容器中的元素 容器类一般向外提供获取迭代器的接口: crbegin() 获取容器的正向迭代器的首部(第一个元素) crend() 获取容器的正向迭代器的尾部(最后一个元素的后面)
迭代器都是可以进行++操作的,反向迭代器和正向迭代器的区别:
当正向迭代器进行++操作时,迭代器会指向容器的下一个元素位置
当反向迭代器进行++操作时,迭代器会指向容器的上一个元素位置
迭代器的 ++it 相对于 it++ 的操作,程序的效率会更高
在实现迭代器对象的后置++时,会产生一个局部的临时对象(参考运算符重载)
通过迭代器可以读取和修改它指向的元素*迭代器对象名称
就表示迭代器指向的元素
cout << *it <<endl; // 读取*it = 1024; // 修改
注意:(1) 大部分的容器都实现了迭代器,但是容器适配器(stack、queue、priority_queue)没有迭代器,但是容器适配器中有些成员函数可以用来操作容器指定元素
适配器模式:可以根据相似的类,进行某些功能的删除或者修改,适配得到一个新的类(新类型的内部是由老类型实现的),这个就是适配器模式如:stack是一个先进后出的栈,stack内部就可以使用vector实现,就成为了一个栈(符合先进后出的特定)
(2) 不同的迭代器功能强弱不一样,迭代器的功能强弱,决定了该容器是否支持STL中的某些算法!!!
如:排序算法需要通过随机访问迭代器来访问容器中的元素,因此有些容器不支持排序算法
常用的迭代器按照功能强弱:
迭代器都拥有基础的功能:== != ++ * ......
1. 单向迭代器(只能++)
it++ / ++it2. 双向迭代器(能++ / 能--)
it++ / ++it / it-- / --it3. 随机访问迭代器(能自增自减,还能it = it+n)
类似于指针的特性
能够
it++ / ++it / it-- / --it
也能:一次性移动i个位置
it = it + i
it = it - i
此外随机访问迭代器 it1 和 it2 能够使用<、>、>=、<=、==等进行直接比较如果 it1 < it2成立,说明 it1 经过 i 次自增能够和 it2 表示同一个位置
常用的容器支持的迭代器:array 静态数组 随机访问迭代器
vector 可变长数组 随机访问迭代器
deque 双端队列 随机访问迭代器
list 双向链表 双向迭代器
set / multiset 双向迭代器
map / multimap 双向迭代器
stack 不支持迭代器
queue 不支持迭代器
....#include <iostream> #include <array> #include <list> using namespace std; int main() { // array是一个固定大小的数组,不能动态改变大小 // array<int, 10> a = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; array<int, 10> a{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; // auto it = a.begin(); array<int, 10>::iterator it = a.begin(); while (it != a.end()) { cout << *it << " "; *it = 2 * (*it); ++it; // 比it++ 效率更高 } cout << endl; //只有实现了迭代器的容器,才可以使用新式for循环 for (auto it : a) { cout << it << " "; } cout << endl; cout << "=============================" << endl; array<int, 10>::reverse_iterator it1 = a.rbegin(); while (it1 != a.rend()) { cout << *it1 << " "; *it1 = 2* (*it1); ++it1; } cout << endl; for (auto it : a) { cout << it << " "; } cout << endl; array<int, 10>::iterator it2 = a.begin(); it2 += 3; cout << *it2 << endl; list<int> l{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; // auto it3 = l.begin(); list<int>::iterator it3 = l.begin(); // it3 += 3; // list的迭代器不支持随机访问 cout << *it3 << endl; return 0; }
迭代器辅助函数(算法):迭代器库---->迭代器操作
STL中有常用的三个操作迭代器的函数模板
p和q都是迭代器,n是整数
advance(p, n) 使迭代器p向前或者向后移动n个位置,无返回值
迭代器必须有双向迭代器的功能distance(p, q)
计算两个迭代器之间的距离,即迭代器p经过多少次++后能够和q相等
如果调用前,p已经在q的后面,如果满足随机访问迭代器,则返回p-->q距离的负数,如果不满足随机访问迭代器,则返回q到p的距离
next 令迭代器自增prev 令迭代器自减,必须满足双向迭代器的要求
iter_swap(p, q) 交换两个迭代器p,q指向的位置
使用上面的函数,需要加上头文件<algorithm>迭代器失效(迭代器非法化):
迭代器表示一个位置,但是我们对容器的某些操作,可能会影响迭代器表示的值
操作:插入/删除/增加元素......
Linux和Windows失效的方式不一样!!!
迭代器失效是序列式容器的一大特点(因为内存是连续的)对于序列式容器(如:vector/deque)实现的方式大多是连续的内存空间,删除当前的iterator指向的元素
可能会使得容器中后面的元素内容改变,造成表示当前元素后面的迭代器都失效这是因为vector/deque使用了连续分配的内存,删除一个元素会使得后面所有的元素都向前移动一个位置
所以不能使用erase(it++)的方式,但是erase可以返回下一个有效的iterator在迭代器的后面插入一个元素,可能会造成迭代器失效,也可能不会(空间不够:扩容)
数组型数据结构:元素是分配在连续的内存中,insert和earse等操作,都会使得删除点和插入点后面的元素
变动位置,所以插入点和删除点后面的迭代器都有可能失效链式型数据结构:元素是分配在不连续的内存中,insert和earse等操作,只会让删除点和插入点当前的迭代器失效
#include <iostream> #include <vector> #include <list> #include <algorithm> #include <array> using namespace std; int main() { list<int> l{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; list<int>::iterator p = l.begin(); // 1 cout << *p << endl; advance(p, -3); cout << *p << endl; // 9 list<int>::iterator q = l.end(); cout << distance(p, q) <<endl; // q已经在后面了,如果满足随机访问迭代器,则返回p-->q距离的负数 // 如果不满足随机访问迭代器,则返回q到p的距离 // cout << distance(q, p) << endl; // 9 list不支持随机访问迭代器 // array<int, 5> aa{1, 2, 3, 4, 5}; // cout << distance(aa.end(), aa.begin()) << endl; // -5 vector<int> a{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; vector<int>::iterator it = a.begin(); it = it + 3; // 4 cout << *it << endl; a.erase(a.begin()); // 会使得某些迭代器失效 cout << *it << endl; // 5 a.erase(it++); cout << *it << endl; // 7 it = a.erase(it); // 返回删除的元素的下一个有效元素的迭代器 cout << *it << endl; // 8 return 0; }
重点:迭代器和指针之间的区别!!!
02 容器
容器就是用来存储并且管理某一类对象的集合,是使用类模板实现的常用的数据结构
常见的有静态数组、可变长数组、链表、栈、队列、二叉树.....等数据结构在STL中都实现为模板类
在使用容器类模板的时候,需要指明容器中存放的元素类型是什么(模板类实例化的时候需要类型参数)
如:list<int> l{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
array<int, 10> a{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
......
容器可以存放基本数据类型,也可以存放自定义的数据类型(自己实现的类类型),对象或者基类类型的变量加入容器的时候,实际上加入到容器的是对象/变量的副本(自定义的类型要能够整体的拷贝和赋值)
STL中许多的算法,如:排序、查找...算法,在执行的过程中会对容器中的元素进行比较(<、<=、==......),元素在比较的时候,通常会使用运算符(被放到容器中的类型最好要实现必须使用的运算符)
容器分为两大类:顺序容器:
如:数组(array、vector、deque(双端数组/双端队列))、链表(list)、栈(stack)、队列(queue)
之所以被叫做顺序容器是因为元素在容器中的位置与元素的值无关(和插入到容器的顺序有关),即容器本身不是排序的,将元素加入到容器的时候,指定在什么位置,元素就位于什么位置关联容器(底层使用排序二叉树实现的,关联容器中的元素是自动排序的):
集合(set)以及映射(map)
set、multiset、map、multimap
关联容器中的元素是自动排序的,插入元素的时候,容器会自动按照一定的顺序将元素放到合适的位置
因为插入元素时,不能指定位置(所以放到关联容器内的数据类型要能够使用<、<=比较)默认情况下,关联容器中的元素是从小到大排序的,是使用"<"进行比较的(如果是自己实现的类型就需要提供重载的运算符函数)
因为关联容器是排好序的,所以关联容器的查找性能非常好
03 基本的顺序容器的用法介绍
array
静态数组类型
需要包含头文件
#include <ayyay>
实例化方法(构造函数):array<int, 5> arr1;
array<int, 10> arr2 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
array<int, 10> arr3{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
array<string, 2> arr4 = {"hello", "world"};
元素访问:at 访问指定的元素,同时进行越界检查
cout << arr2.at(0) << endl;
arr2.at(0) = 10;
operator[] 通过下标的方式访问指定的元素
cout << arr2[1];
arr[2] = 1024;
front 访问第一个元素
arr2.front();
back 访问最后一个元素
arr2.back();
data 直接返回底层存储元素的空间的首地址
int *pd = arr2.data();
迭代器:是一种思想,是为了方便让用户简单的遍历容器中的元素
以下是array给用户提供的获取迭代器的方法
begin();
end();
cbegin();
cend();
rbegin();
rend();
crbegin();
crend();
容量:
empty() 检查容器是否为空
size() 返回容纳的元素数量
max_size() 返回最大的容量
操作:
fill 以指定的值填充数组
swap 交换两个数组的所有内容
非成员函数:
operator== 比较,会按照数组每一个元素进行比较
vector 可变长数组
添加头文件:
#include <vector>
构造函数:
vector<int> a1;
vector<string> a2{10, "hello"};
vector<int> a{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
元素访问:
at 访问指定的元素,同时进行越界检查
a.at(0); ===> 1
a.at(1) = 100;
operator[] 访问指定的元素
a[0]
a[1] = 100;
front 访问第一个元素
a.front()
===>
a[0] / a.at(0);
back 访问最后一个元素
a.back() = 1024;
cout << a.back() << endl;
data 直接访问底层数组
int *pb = a.data();
迭代器:是一种思想,是为了方便的让用户可以简单的遍历容器中的元素
以下是vector给用户提供的获取迭代器的方法
begin();
end();
cbegin();
cend();
rbegin();
rend();
crbegin();
crend();
容量:
empty() 检查容器是否为空
if (a.empty()) {
cout << "容器为空" << endl;
}
size 返回容纳的元素数
a.size();
max_size 返回可容纳的最大元素数
a.max_size();
reserve 预留存储空间
a.reserve(100);
capacity 返回当前存储空间能够容纳的元素数
shrink_to_fit 通过释放未使用的内存减少内存的使用
修改器:
clear 清除内容
a.clear();
insert 插入元素 <------
emplace 原位构造元素
erase 擦除元素
push_back 将元素添加到容器末尾
emplace_back 在容器末尾就地构造元素
pop_back 移除未元素
resize 改变容器中可存储元素的个数
swap 交换内容
非成员函数(一个元素一个元素的比较)
operator==
operator!=
operator<
operator<=
operator>
operator>=
operator<=>
#include <iostream> #include <vector> #include <list> #include <iterator> using namespace std; int main() { vector<int> a1; for (auto it : a1) { cout << it << " "; } cout << endl; vector<string> a2{10, "hello"}; for (auto it : a2) { cout << it << " "; } cout << endl; int x = 10; vector<int> a3{5, x}; for (auto it : a3) { cout << it << " "; } cout << endl; vector<int> a{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; a.reserve(100); cout << "size:" << a.size() << endl; cout << "capacity:" << a.capacity() << endl; a.back() = 1024; cout << a.back() << endl; for (auto it : a) { cout << it << " "; } cout << endl; cout << a.max_size() << endl; return 0; }
deque 可变长数组
list 双向链表
forward_list 单向链表
stack 栈
queue 队列
priority_queue 带优先级的队列(不是先进先出)
1. 重点了解,各种顺序容器之间的差别
如:
vector 和 array
vector 和 deque
deque 和 list
queue 和 priority_queue
......
2. 容器的选择(1) 强调快速随机访问,则 vector 优于 list
(2) 以知要存储的元素个数,则 vector 比 list 好
(3) 强调增和删且不要在两端插入和修改,则 list 比 vector 更好
(4) 需要在首部插入和删除元素,则 deque 比 vector 好
04 关联容器(set / map内部是排好序的,和加入元素的顺序无关)
集合(set)是一种包含已经排序好的对象的关联容器
set / multiset会根据待定的排序规则(默认是从小到大),自动的将元素排序,两者不同在于前者不允许元素重复,而后者允许元素重复
set<int> s;set<string> s1; // 逐个字母比较
......
set<Test> s2; // Test 里面实现了比较规则,能够调用> / <
注意:1. 不能直接修改元素值,因为会打乱原本正确的顺序,要改变原始数据的值必须先删除旧元素,再插入新元素
2. 不提供直接操作元素的任何函数,直接通过迭代器进行间接存取,而且从迭代器的角度看,元素值是常数(不可以直接修改)
set 是使用红黑树实现的(平衡二叉树),内部的查找性能非常好
主要用来存储一些没有顺序的用户数据
用户能够自由的插入和查找
#include <iostream> #include <set> using namespace std; int main() { // 实例化set对象,里面的元素类型是string set<string> a; a.insert("cat"); a.insert("dog"); a.insert("horse"); a.insert("haha"); a.insert("aaaaa"); // set<string>::iterator it = a.begin(); // while (it != a.end()) { // cout << *it << ' '; // ++it; // } for (auto it: a) { cout << it << ' '; } cout << endl; for (set<string>::iterator it = a.begin(); it != a.end(); ++it) { cout << *it << ' '; // *it = "bbb"; // set不允许修改元素 } cout << endl; cout << "empty:" << a.empty() << endl; cout << "size:" << a.size() << endl; // struct pair { // T1 first; // KEY // T2 second; // VALUE // }; std::pair<set<string>::iterator, bool> res = a.insert("zhangsan"); cout << *(res.first) << endl; // 访问pair的第一个数据,指向插入后的数据 cout << res.second << endl; // 是否插入成功 res = a.insert("zhangsan"); cout << *res.first << endl; // 访问pair的第一个数据,指向插入后的数据 cout << res.second << endl; // 是否插入成功 a.insert({"111", "222", "3333"}); a.erase("111"); for (auto it : a) { cout << it << ' '; } cout << endl; return 0; } aaaaa cat dog haha horse aaaaa cat dog haha horse empty:0 size:5 zhangsan 1 zhangsan 0 222 3333 aaaaa cat dog haha horse zhangsan
05 map(映射)
map是关联容器中的一种,map里面存储的元素一般是一个一个的键值对,在关联容器中,对象的位置取决于和它关联的键的值(以pair中的第一个数据排序)
键可以是基本类型,也可以是类类型,字符串经常被用来作为键
如:手机通讯录(名字和号码就是一个键值对)
名字可能是一个字符串,对应着一个确定的电话号码,这种形式的数据叫做键值对
称为pair类型的对象
账号---密码也是键值对
pair 也是一个类模板,它包含两个成员变量,成员变量的类型可以任意指定
pair 的第一个成员叫做first,第二个成员叫做second
pair 的大致结构如下:template<typename T1, typename T2>
class pair {
T1 first; // KEY
T2 second; // VALUE
};
map最主要的作用也是查找,通过 pair 的一个元素(键)可以很快的找到与之关联的第二个元素(值),一般的我们把第一个元素叫做Key,第二个元素叫做value
所以,一个pair结构就是一个键值对,map容器中存储的就是一个一个的键值对map<K, T>类模板定义在头文件map中,它定义了一个保存T类型的map,每一个T类型的对象都有一个与之关联的K类型的键,容器中元素位置是根据键排序的,可以使用键在容器中查找键对应的值
====>
Key是不能重复了,而Value是可以重复的
map和multimap唯一的区别在于,map的键值不可以重复,而multimap的键值可以重复
#include <iostream> #include <map> #include <iterator> using namespace std; // -std=c++14 auto print = [](const auto & m) { for (auto it : m) { cout << it.first << "," << it.second << endl; } }; int main() { // map<string, int> m; // m["zhangsan"] = 60; // 通过下标的形式往m中插入键值对 <"zhangsan",60> // m["lisi"] = 70; // <"lisi",70> // m["kunkun"] = 80; // cout << m.size() << endl; // for (map<string, int>::iterator it = m.begin(); it!= m.end(); ++it) { // cout << it->first << "," << it->second << endl; // } // cout << "====================" << endl; // m["kunkun"] = 98; // 如果下标不存在,则是插入数据,如果下标存在,则是修改“键”对应的值 // for (auto it : m) { // cout << it.first << "," << it.second << endl; // } // 创建一个容器对象 map<string, string> ssmap; cout << "size:" << ssmap.size() << endl; // 插入键值对 ssmap["you"] = "你"; // <"you","你"> []里面的一般不叫做下标,而是叫做key ssmap.insert({"hello", "你好"}); ssmap.insert(make_pair("how", "如何")); ssmap.insert(pair<string, string>("world", "世界")); // 输出键值对 for (auto it : ssmap) { cout << it.first << "," << it.second << endl; } cout << "size:" << ssmap.size() << endl; // 利用lamda表达式输出 print(ssmap); // 修改 ssmap["you"] = "你真棒"; print(ssmap); // 可以使用at函数查找给定键对应的值,如果不存在,则抛出out_of_range异常 try { // cout << ssmap.at("KKK") << endl; auto value = ssmap.at("you"); cout << "value:" << value << endl; } catch (out_of_range & e) { // catch (exception & e) 也可以捕获 cout << e.what() << endl; } // 使用find函数查找给定键对应的值,找到了则返回对应键值对的迭代器,否则返回end auto it = ssmap.find("you"); if (it != ssmap.end()) { cout << "find:" << it->first << "," << it->second << endl; } return 0; }
06 常用算法
指的是使用函数模板实现的常用的算法,如:排序,查找,交换,乱序...
算法一般是用来处理集合中的元素,可以出于不同的目的去查找、排序、和修改集合中的元素,所以容器的迭代器都提供一致的操作,通过迭代器的协助,算法程序可以作用于任何容器
STL提供了能够在各种容器中使用的算法,大约有70多种....
许多算法操作的是容器的一个区间(也可以是整个容器),因此算法可能需要两个/两个以上的参数,一个是区间的起点,一个是区间的终点,可能还需要一个"谓词对象(可调用对象)"来形容一个条件!!!
有的算法会返回一个迭代器如:find_if 算法在容器中查找一个符合条件的元素
并且返回一个指向该元素的迭代器,如果没找到则返回区间末尾的迭代器,说明遍历完了还没有找到符合条件的元素
有的算法会修改容器指定数据:如:
copy 将一个容器指定内容复制到另一个容器
copy_if 将一个容器中符合条件的内容复制到另一个容器
remove 在容器中删除一个元素
random_shuffle 随机打乱容器中的元素
....
有的算法不会修改容器中的数据
如:find 在容器中查找符合条件的元素
count_if 统计容器中符合条件的元素(需要一个一元谓词)
注意掌握下面几个算法:
sort
前面两个参数是排序的区间,第三个参数可以省略
如果不省略,提供一个可调用对象(函数/函数指针/函数对象/lambda),说明排序规则(接受一个二元谓词)
find
查找指定的数据
find_if
前面两个参数是查找的区间,第三个参数是你要查找的值,是一个可调用对象,描述了查找条件
会找到第一个让可调用对象返回值的元素的迭代器count
统计指定的数据的个数count_if
统计容器中符合条件的元素数量,条件就是使用可调用对象描述!
不修改序列的操作
修改序列的操作
划分操作
排序操作
#include <iostream> #include <vector> #include <list> #include <iterator> #include <algorithm> using namespace std; // 用自定义函数对象排序(函数对象,重载了()) struct { bool operator()(int a, int b) const { return a < b; } } customLess; // 利用类型定义了一个全局变量 int main() { // s是一个集合对象 vector<int> s{67, 7, 4, 9, 15, 2, 6, 34, 100, 56}; // 默认是升序排序 std::sort(s.begin(), s.end()); for (auto it : s) { std::cout << it << " "; } std::cout << '\n'; // 用标准库比较函数对象排序 // std::greater<int>()是一个函数对象 从大到小排序 // std::less<int>()是一个函数对象 从小到大排序 std::sort(s.begin(), s.end(), std::greater<int>()); for (auto it : s) { std::cout << it << " "; } std::cout << '\n'; std::sort(s.begin(), s.end(), customLess); for (auto it : s) { std::cout << it << " "; } std::cout << '\n'; // 用 lambda 表达式排序 std::sort(s.begin(), s.end(), [](int a, int b) { return a > b; }); for (auto it : s) { std::cout << it << " "; } std::cout << '\n'; return 0; }
#include <iostream> #include <vector> #include <algorithm> using namespace std; bool fun(int x) { cout << "hahahaha" << endl; return x < 5; } int main() { std::vector<int> v{1, 2, 3, 4, 4, 3, 7, 8, 9, 10}; // 确定 std::vector 中有多少个整数匹配目标值 int num1 = std::count(v.begin(), v.end(), 3); int num2 = std::count(v.begin(), v.end(), 5); std::cout << "number: " << 3 << " count: " << num1 << '\n'; std::cout << "number: " << 5 << " count: " << num2 << '\n'; // 用 lambda 表达式计量能被 3 整除的元素数 int num3 = std::count_if(v.begin(), v.end(), [](int i) { return i % 3 == 0; }); std::cout << "number divisible by three: " << num3 << '\n'; // 查找大于3的元素数量 int res = std::count_if(v.begin(), v.end(), [](int x) { return x > 3; }); std::cout << res << '\n'; // 查找小于5的元素数量 res = std::count_if(v.begin(), v.end(), fun); std::cout << res << '\n'; return 0; } number: 3 count: 2 number: 5 count: 0 number divisible by three: 3 6 hahahaha hahahaha hahahaha hahahaha hahahaha hahahaha hahahaha hahahaha hahahaha hahahaha 6