C++容器算法
容器算法
<algorithm>
是c++
自带的容器算法,提供一系列实用的算法。在谈到容器算法,我们大概率会用到谓词predicate
,谓词返回的类型是布尔类型(bool)
可以是lambda
表达式、函数对象以及其它可调用的对象。一般在实现中,predict
是一元谓词,compare
是二元谓词。
查找
-
find()
查找元素find
接受三个参数,第三个参数是值类型,set
、map
自带count
函数也能实现这样的功能,返回值0
表示不存在。为了方便本次连带find_if
、find_if_not
、find_first_of
、find_end
和adjacent_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;
-
all_of()
基于find_if_not
实现,支持一个一元谓词。all_of(a.begin(), a.end(), [](auto x){return x != 3;}); // 判断所有元素是否满足条件,返回值是bool值
-
any_of()
基于none_of
实现,none_of
基于find_if()
实现,non_of(a.begin(), a.end(), [](auto x){return x != 3;}); // 判断所有元素是否都不满足条件 any_of(a.begin(), a.end(), [](auto x){return x != 3;}); // 任意数量的元素满足条件都返回true
去重
-
unique(nums.begin(), nums.end())
除掉连续相同的值unique
函数的作用是删除掉的连续的相同的值,unique还支持传入二元谓词(可以理解为一个参数的函数),会返回一个迭代器it
,但是并不是end()
,中间是未指定的值(访问可能会产生未定义行为)。注意不要用set
和map
,返回的都是常量迭代器是无法改变的,即其指向的元素无法改变。// 无谓词写法 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
用于求算两个迭代器之间的差值,只用于同一容器,set
,map
,vector
都可以用。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
,谨慎使用map
和set
,不一定可用。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()
反向copy
能够将容器中元素复制到输出迭代器之前。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()迭代器之前没有意义。
-
copy_n()
复制n
个元素到结果容器,不能使用map
和set
copy_n(a.begin(), n, b.begin()); // 底层是copy()实现
-
copy_if()
,不能使用map
和set
copy_n(a.begin(), a.end(), b.begin(), greater<>); // 二元谓词
全排列
next_permutation
提供返回容器的一个全排列,也支持一个二元谓词。
next_permutation(a.begin(), a.end());
取集合
set_intersection
取交集、set_union
取并集、set_difference
取单侧差集,set_symmetric_difference
取两侧差集,值得注意的是set_difference
在输出到结果容器中只会输出第一个容器中的元素(不管第一个容器的元素是否少于第二个容器的元素)。这四个函数都接受一个二元谓词。注意: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
接受一个二元谓词,两个函数都能够使用set
和map
。search
接受一个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}
计数
count
计数特定值的数量,count_if
也存在一个接受一元谓词。count_if
和count
,所有map
和set
也不可用。
vector<int> a{1,1,1,4};
vector<int> b{1,2};
int num1 = count(a.begin(), a.end(), 1);
int num2 = count_if(a.begin(), a.end(),
[](auto a){return a > 0;});
cout << num1 << endl; // 3
cout << num2 << endl; // 4
限制数值范围
clamp
用于限制数值的范围在特定范围内,支持任意类型的val
。如果val
比lo
低,则返回lo
;如果val
介于lo
和hi
之间,返回val
;如果val
大于hi
,返回val
。同样也接受一个二元谓词。
int x = clamp(val, 1o, hi); // 底层使用min和max实现,min(max(val, lo), hi)
填充
fill
和fill_n
,可用于初始化特定容器,避免未定义错误, map
和set
无法使用。
void Print(const array<int, 5>& a)
{
for_each(a.begin(), a.end(), [](auto item){cout << item << ":";});
}
int main()
{
array<int, 5> a;
fill(a.begin(), a.end(), 0);
fill_n(a.begin(), 3, 1);
Print(a); // {1,1,1,0,0}
}
等于
equal()
适用于判断两个序列是否相等,接受一个二元谓词,可以用于map
和set
。
map<int, int> a{{1,2}, {3, 4}};
map<int, int> b{{1,2}, {3, 4}};
bool flag = equal(a.begin(), a.end(), b.begin(), b.end());
if (flag == true) cout << "Yes" << endl;
二分查找
binary_search()
基于lower_bound()
实现二分法找到特定值,返回值为bool
,支持一个二元谓词,所以将upper_bound()
也放在一起讲解。三个函数都已使用set
,但是要注意set
不支持迭代器直接减。upper_bound()
返回的是第一个大于特定元素值的迭代器位置。lower_bound()
, 用于查找第一个不小于特定值的元素,返回迭代器。
vector<int> a{1, 2, 4, 6, 10};
auto it = binary_search(a.begin(), a.end(), 5);
if (it == true) cout << "Yes" << endl;
it = upper_bound(a.begin(), a.end(), 5);
cout << distance(a.begin(), it) << endl; // 3指向6的位置
// 以下也可以
cout << it - a.begin() << endl; // vector等类似容器可直接减
// 这样也是可以的
set<int> a{1, 2, 4, 5, 6, 10};
auto it = upper_bound(a.begin(), a.end(), 5); // it指向6
it = lower_bound(a.begin(), a.end(), 5); // it指向5
cout << distance(a.begin(), it)<< endl;
移除
remove()
底层是基于find_if
实现的,接受一个一元谓词predict
。不可用于map
和set
,因为会更改常量迭代器指向的元素。
vector<int> a{1, 2, 2, 6, 7, 9};
auto it = remove(a.begin(), a.end(),2); // 返回值是移除后序列最后一个元素的下一个迭代器。
cout << a[4] << endl; // 返回7,这是因为实际上,移除元素是将其后的元素复制到删除的位置,所以map和set不可用
cout << it - a.begin() << endl; // 4
cout << a.size() << endl; // 6 因为空间并未删除,实际上还是有6个元素,所以size不变
cout << a.capacity() << endl; // 6,此时空间也是6
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 不到万不得已,千万不要去外包
· C# WebAPI 插件热插拔(持续更新中)
· 会议真的有必要吗?我们产品开发9年了,但从来没开过会
· 【译】我们最喜欢的2024年的 Visual Studio 新功能
· 如何打造一个高并发系统?