Hi,大家好,我是编程小6,很荣幸遇见你,我把这些年在开发过程中遇到的问题或想法写出来,今天说一说
C++ STL算法多个视角的分类,希望能够帮助你!!!。
STL算法是用来操作容器(container)中的数据(容器元素)的模板函数,通常以一对容器的迭代器作为操作区间。通常STL算法都涉及到比较操作(相等比较或大小比较),STL算法通常通过一个函数对象来定制其中的比较操作,让算法代码更加泛化,更加通用。
TTL提供了大约100个这样的模板函数,主要是由头文件<algorithm>、<functional>、<numeric>组成。
<algorithm>是所有STL头文件中最大的一个,范围涉及比较、交换、查找、遍历、复制、修改等方面的算法。
<numeric>体积较小,只包括几个在序列上进行简单数学运算的模板函数。
accumulate Accumulate values in range (function template )
adjacent_difference Compute adjacent difference of range (function template )
inner_product Compute cumulative inner product of range (function template )
partial_sum Compute partial sums of range (function template )
iota Store increasing sequence (function template )
<functional>定义了一些模板类,用来声明内建函数对象(仿函数)。
STL可以从多个视角进行分类:
也可以按不同的视角分为以下七类:
有的算法可能同时属于多个分类。
许多算法都是重载的,有不止一个版本。
实际上,大多数重载的算法都有两个版本,其中一个用==判断元素是否相等,或用<比较大小;而另一个版本多出来一个类型参数 Pred 以及函数形参 Pred op,该版本通过表达式op(x, y)的返回值是 true 还是 false 来判断 x 是否“等于”y 或者“小于”y。
在STL算法分类中有两个普遍存在的后缀:
_if
_copy
其中这两个后缀的作用分别是:
一、对于_if,如果算法存在两种形式,参数的个数相同,其中一种形式的参数要求传递一个值,而另一种形式则会要求传递一个函数或仿函数(函数对象),那么则没有_if后缀的形式要求传递数值,有_if后缀的则要求传递函数。而且传递的函数一般都会是一个一元或二元的判别式。
二、_copy后缀则表示在此算法中元素在被复制到目标区间的同时会被处理,而源序列则保持不变。
非变动性算法主要是指不会改变元素的次序和改变元素值的操作,典型的操作是:
计数 count;
求最值(min_element,max_element);
搜寻某个特定元素 find;
搜寻某段特定元素 search_n;
搜寻某个子区间(search,find_end);
搜寻第一对连续相等的元素 adjacent_find;
其次剩余的就是匹配和比较算法了:判断两个连续的区间是否相等equal、在两个序列中返回第一对不相等的元素。
for_each():
#include<iostream> #include<vector> #include<algorithm> using namespace std; //1 常用遍历算法for_each //普通函数 void print01(int val) { cout << val << " "; } //仿函数 class print02 { public: void operator()(int val) { cout << val << " "; } }; void test01() { vector<int> v1; v1.push_back(11); v1.push_back(22); v1.push_back(33); v1.push_back(44); v1.push_back(55); //最后一项参数是函数指针 for_each(v1.begin(), v1.end(), print01); cout<< endl; cout << "-----------------------" << endl; for_each(v1.begin(), v1.end(), print02()); // for each element 调用函数对象 } int main() { test01(); return 0; }
find():
#include<iostream> #include<vector> #include<algorithm> #include<string> using namespace std; void test01() { //1 查找基本数据类型 vector<int> v1; v1.push_back(11); v1.push_back(33); v1.push_back(55); v1.push_back(77); v1.push_back(99); v1.push_back(111); v1.push_back(133); int number; cout << "请输入要查找的数:" << endl; cin >> number; //find返回一个迭代器 vector<int>::iterator it = find(v1.begin(), v1.end(), number); if (it == v1.end()) cout << "没有找到!" << endl; else cout << "找到了:" << *it << endl; } class Person { public: string m_Name; int m_Age; public: Person(string name, int age) { this->m_Name = name; this->m_Age = age; } //重载==号 让底层的find()指导如何对比Peson的数据类型 bool operator==(const Person& p){ if (this->m_Name == p.m_Name && this->m_Age == p.m_Age) return true; else return false; } }; void test02() {//2 查找自定义数据类型 vector<Person> v; Person p1("羊巅峰", 20); Person p2("羊无敌", 30); Person p3("风清羊", 26); Person p4("羊咩咩", 38); v.push_back(p1); v.push_back(p2); v.push_back(p3); v.push_back(p4); //自定义数据类型的 vector<Person>::iterator it = find(v.begin(), v.end(), p2); if (it == v.end()) cout << "没有找到!" << endl; else cout << "找到了:" << endl << "姓名:" << ( *it).m_Name << " 年龄:" << (*it).m_Age << endl; } int main() { test01(); test02(); getchar();getchar(); return 0; }
find_if():
#include<iostream> #include<vector> #include<algorithm> using namespace std; //常用查找算法 find_if //1 查找基本数据类型 class GreaterFive { public: bool operator()(int val){ return val > 20; } }; void test03(){ vector<int> v; for (int i = 0; i < 10; i++) v.push_back(i); vector<int>::iterator it = find_if(v.begin(), v.end(), GreaterFive()); if (it == v.end()) cout << "没有找到" << endl; else cout << "找到大于5的数字:" << *it << endl; } //2 查找自定义数据类型 class Person { public: string m_Name; int m_Age; public: Person(string name, int age) { this->m_Name = name; this->m_Age = age; } }; //谓词 class Greater20 { public: bool operator()(Person& p){ return p.m_Age > 20; } }; void test04() { vector<Person> v; Person p1("羊巅峰", 19); Person p2("羊无敌", 30); Person p3("风清羊", 18); Person p4("羊咩咩", 37); v.push_back(p1); v.push_back(p2); v.push_back(p3); v.push_back(p4); //找年龄大于20的人 vector<Person>::iterator it = find_if(v.begin(), v.end(), Greater20()); if (it == v.end()) cout << "没有找到" << endl; else{ cout << "找到大于20岁的羊:" << endl; cout << "姓名:" << it->m_Name << " 年龄:" << it->m_Age << endl; } } int main() { test03(); test04(); getchar();getchar(); return 0; }
变动性算法指的是要么直接改变元素值,或者在复制到目标区间的过程中改变元素值且原区间不发生改变。
首先是两个特殊的算法:复制和合并:
copy 从第一个元素开始复制到目标区间;
merge 合并两个区间;
其次就是遍历操作每一个元素for_each;
遍历+操作+复制 transform(遍历操作复制后原区间不会变,操作的结果覆盖到目标区间,注意既可以对一个原区间间进行一元操作,也可以同时对两个原区间进行二元操作后将结果覆盖到目标区间);
最后就是替换操作:可以批量替换区间的每一个元素:
fill 以给定值替换区间的所有元素;
generate 以某项操作的结果替换区间所有元素;
替换区间中N个元素:
fill_n 以给定值替换区间的前N个元素;
generate_n 以某项操作的结果替换区间的前N个元素;
以另一个元素替换某个特定元素replace。
merge():
#include<iostream> #include<vector> #include<functional> #include<algorithm> using namespace std; //merge void print(int val) { cout << val << " "; } void test01() { vector<int> v1; vector<int> v2; for (int i = 0; i < 10; i++) { v1.push_back(i); v2.push_back(i + 6); } vector<int> v3; //提前给目标容器分配空间 v3.resize(v1.size() + v2.size()); merge(v1.begin(), v1.end(), v2.begin(), v2.end(), v3.begin()); for_each(v3.begin(), v3.end(), print); cout << endl; } int main() { test01(); getchar(); return 0; }
replace():
#include<iostream> #include<vector> #include<algorithm>//replace using namespace std; class MyPrint { public: void operator()(int val){ cout << val << " "; } }; void test01() { vector<int> v; v.push_back(10); v.push_back(20); v.push_back(20); v.push_back(20); v.push_back(30); cout << "替换前:" << endl; for_each(v.begin(), v.end(), MyPrint()); cout << endl; replace(v.begin(), v.end(), 20, 66); cout << "替换后:" << endl; for_each(v.begin(), v.end(), MyPrint()); cout << endl; } int main() { test01(); getchar(); return 0; }
移除性算法可以移除某区间内的特定元素也可以在复制到目标区间的过程中去执行移除动作。
不过需要特别注意的是:除了在复制的过程中移除外,其他的移除事实上都只是在逻辑上移除元素,具体的手段是:将不需要被移除的元素往前移覆盖应该被移除的元素,因此这些操作并不改变操作区间内的元素个数,而是返回逻辑上的新终点位置。
移除性算法中最重要就两个操作:
一是移除某些特定的元素(包括移除和复制移除);
remove 移除特定值的元素;
remove_if 移除满足条件元素;
remove_copy 复制移除特定元素;
remove_copy_if 复制移除满足特定条件的元素。
二是移除相邻的重复元素:
unique 移除相邻的重复元素;
unique_copy 复制并移除相邻的重复元素。
注意如果想通过unique算法移除掉序列里的所有重复元素则必须先对序列排序,使其成员有序序列后则用unique移除相邻重复的元素后才能使该序列不包含重复的元素。
remove():
// remove algorithm example #include <iostream> // std::cout #include <algorithm> // std::remove int main () { int myints[] = {10,20,30,30,20,10,10,20}; // 10 20 30 30 20 10 10 20 // bounds of range: int* pbegin = myints; // ^ int* pend = myints+sizeof(myints)/sizeof(int); // ^ pend = std::remove (pbegin, pend, 20); // 10 30 30 10 10 ? ? ? // ^ ^ std::cout << "range contains:"; for (int* p=pbegin; p!=pend; ++p) std::cout << ' ' << *p; std::cout << '\n'; getchar(); return 0; } // range contains: 10 30 30 10 10
变序性算法指的是改变序列中元素的顺序但不改变元素的值。
变序算法中最主要的几个操作是:逆转、旋转、分步排序、随机乱序,部分前移。
逆序算法:
reverse 将元素次序逆转;
reverse_copy 复制的同时逆转元素顺序;
旋转元素次序:
rotate 按参数旋转元素顺序;
rotate_copy 复制的同时旋转元素次序;
分步排序:
next_permutation 得出朝向降序的下一步变换序列、
prev_permutation 得出朝着升序的下一步变换序列;
随机乱序:
random_shuffle将元素次序随机打乱。
部分前移算法:
partition 使满足特定条件的元素移到序列的前面;
stable_partition与partition一样也是使满足特定条件的元素移到序列的前面,但是stable_partition保存满足条件和不满足条件的各个元素间的原有相对位置不变。
next_permutation():
#include <iostream> #include <algorithm> using namespace std; int main() { int num[3]={1,2,3}; do { cout<<num[0]<<" "<<num[1]<<" "<<num[2]<<endl; }while(next_permutation(num,num+3)); return 0; }
排序算法主要是指使序列的全部元素或部分元素或特定位置成为有序的,这与变序算法不同,排序算法中的有序是指排列成升序或降序而变序算法中的有序不一定如此。
排序算法中主要包括的是;对所有位置排序、对部分位置排序、对单个特定位置排序。
对所有位置排序:
sort对序列中的所有元素进行排序;
stable_sort对所有元素进行排序但是保持相等的元素间的原有相对次序。
对部分位置排序:
partial_sort对所有元素排序,直到前n个元素就位;
partial_sort_copy以目标区间的大小为准对原区间进行复制并排序直到保持相对应的前部分位置有序位置。
对单个特定位置进行排序:
nth_element对所有元素排序使第n个位置的相应元素就位,并且使位于位置n之前的元素都小于它,位于位置n之后的元素都大于它。
sort():
#include<iostream> #include<vector> #include<functional> #include<algorithm> using namespace std; //常用排序算法sort void myPrint(int val) { cout << val << " "; } void test01() { vector<int> v1; v1.push_back(100); v1.push_back(33); v1.push_back(51); v1.push_back(77); v1.push_back(99); v1.push_back(101); v1.push_back(11); //利用sort进行升序 sort(v1.begin(), v1.end()); for_each(v1.begin(), v1.end(), myPrint); cout << endl; //降序排序 sort(v1.begin(), v1.end(), greater<int>()); for_each(v1.begin(), v1.end(), myPrint); cout << endl; } int main() { test01(); getchar(); return 0; }
对已序区间的算法主要是根据原区间元素已序的特定采用特定的算法进行相应的操作,使得这些操作一般通用的操作具有更高的效率,但必备的前提都是该用于操作的原区间必须都是已序序列。
已序算法中主要包括:搜寻、区间合并、集合并集、交集、差集、并集-交集。
搜寻算法:
binary_search用二分法搜寻是否包括特定元素;
includes搜寻一个区间是否包含另一个区间的全部元素;
lower_bound搜寻第一个大于等于给定值的元素;
upper_bound搜寻第一个大于给定值的元素;
equal_range搜寻等于给定值的区间范围返回数值对。
区间合并:
merge将两个区间的元素合并;
inplace_merge将两个连续的已序区间合并成一个有序区间。
并集:set_union求两个区间的并集。
交集:set_intersection求两个区间的交集。
差集:set_difference求第一个区间与第二个区间的差集。
并集-交集:set_symmetric_difference求只出现在两个区间之一的元素并形成一个已序区间。
set_intersection():
#include<iostream> #include<vector> #include<functional> #include<algorithm> using namespace std; //set_intersection void print(int val) { cout << val << " "; } int min(int a,int b) { return a<b?a:b; } void test01() { vector<int> v1; vector<int> v2; for (int i = 0; i < 10; i++) { v1.push_back(i);//0到9 v2.push_back(i + 6);//6到15 } vector<int> v3; //存交集 //目标容器提前开辟内存空间 大容器包含小容器 v3.resize(min(v1.size(), v2.size())); //获取交集 返回的是一个结束迭代器 vector<int>::iterator itEnd = set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(), v3.begin()); for_each(v3.begin(), itEnd , print); cout << endl; } int main() { test01(); getchar(); return 0; }
数值运算算法指的是以不同的方式组合数值元素。
数值算法中的主要算法包括(全部包含在<numeric>内):
accumulate组合一个区间所有元素,以传进初始值参数为准可以进行迭加运算,也可以传进特定的操作函数后,以初始值参数进行迭代运算。
inner_product组合两个区间的所有元素,可以是两个区间的对于位置的元素的乘积与初始值参数迭加,也可以传入两个特定参数两个区间的相应位置的元素进行二元操作后在迭代。
adjacent_difference将每个元素和其前的一个元素进行组合,每一个元素都与其前面的一个元素相减后进行特定二元操作后将结果另写入一个目标序列。
partial_sum将每个元素和其先前的所有元素组合,可以是每一个元素与其所有先前的元素累计或累积进行特定操作后将结果保存写入目标区间。
accumulate():
#include<iostream> #include<vector> #include<numeric> using namespace std; //accumulate void test01() { vector<int> v1; for (int i = 0; i <= 100; i++) { v1.push_back(i); } int total_number = accumulate(v1.begin(), v1.end(), 0); cout << total_number << endl; } int main() { test01(); getchar(); return 0; }
ref:
https://zhuanlan.zhihu.com/p/
http://www.cplusplus.com/reference/algorithm/
http://www.cplusplus.com/reference/numeric/
http://www.cplusplus.com/reference/functional/
-End-
今天的分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。
上一篇
已是最后文章
下一篇
已是最新文章