算法主要由头文件<algorithm>
、<functional>
、<numeric>
组成
<algorithm>
是所有STL头文件中最大的一个,范围涉及到比较、交换、查找、遍历操作、复制、修改等
<numeric>
体积很小,只包括几个在序列上面进行简单数学运算的模板函数
<functional>
定义了一些模板类,用以声明函数对象
5.1常用遍历算法
for_each
遍历容器
transform
搬运容器到另一个容器中
5.1.1 for_each
for_each ( iterator beg, iterator end, _func )
遍历容器元素。beg
是开始迭代器,end
是结束迭代器,_func
是函数或者函数对象。当传入的是函数的时候,只需给定函数名称即可,传入的是函数对象的时候,则需要传入实例化的对象。
示例:
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
// 普通函数
void print01(int val)
{
cout << val << " ";
}
// 仿函数
class print02
{
public:
void operator()(int val)
{
cout << val << " ";
}
};
void test01()
{
vector<int> v;
for(int i = 0;i<10;i++)
{
v.push_back(i);
}
// 可以通过迭代器访问容器元素
// 也可以采用for_each算法访问
for_each(v.begin(),v.end(),print01); //普通函数给定函数名称即可
cout << endl;
for_each(v.begin(),v.end(),print02()); // 仿函数则是实例化的函数对象
cout << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
执行结果:
for_each
算法的其实就是通过迭代器,对容器中的数据进行遍历,然后将每个元素传递给形参列表中的普通函数或者仿函数,然后根据函数内容执行相对应的操作。
5.1.2 transform
搬运一个容器中的内容到另一个容器中。
函数原型:
transform (iterator beg1, iterator end1, iterator beg2, _func);
beg1
是源容器的开始迭代器,end1
是源容器的结束迭代器,beg2
是目标容器的开始迭代器。_func
是函数或者函数对象,可以在搬运过程中对元素进行操作,比如加减乘除等。
示例:
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
class Transform
{
public:
int operator()(int val)
{
return val+100; // 可以利用仿函数进行一些运算
}
};
class MyPrint
{
public:
void operator()(int val)
{
cout << val << " ";
}
};
void test01()
{
vector<int> v;
for(int i = 0;i<10;i++)
{
v.push_back(i);
}
vector<int> vTarget;
vTarget.resize(v.size()); // 目标容器一定要提前开辟空间!!!
transform(v.begin(),v.end(),vTarget.begin(),Transform());
for_each(vTarget.begin(),vTarget.end(),MyPrint());
cout << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
运行结果:
v中存放的本来是1-9,利用transform
函数,并且在仿函数中执行+100的操作,然后目标容器中数数据就变成了100-109。
5.2 常用查找算法
算法简介:
find // 查找元素
find_if // 按条件查找元素
adjacent_find // 查找相邻重复元素
binary_search // 二分查找法---查找元素是否存在,效率高
count // 统计元素个数
count_if // 按条件统计元素个数
5.2.1 find
find(iterator beg,iterator end,value);
在beg
到end
的范围内查找指定元素value,如果找到,返回指定元素的迭代器,如果找不到,则返回end
迭代器。
底层实现上是通过迭代器遍历每一个元素,并且将value值和每一个元素进行对比。
示例:
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<string>
// 查找内置数据类型
void test01()
{
vector<int> v;
for(int i = 0;i<10;i++)
{
v.push_back(10*i);
}
vector<int>::iterator pos = find(v.begin(),v.end(),30);
if(pos==v.end())
{
cout << "容器v中没有该元素!" << endl;
}
else
{
cout << "找到了您要找的"<< *pos << endl;
}
}
// 查找自定义数据类型
class Person
{
public:
Person(string name,int age)
{
this->m_Name = name;
this->m_Age = age;
}
// 由于find的底层不知道如何判断自定义数据类型的相等
// 所以需要重载 == 操作符
bool operator==(const Person &p)
{
if(this->m_Name == p.m_Name && this->m_Age == p.m_Age)
{
return true;
}
else
return false;
}
string m_Name;
int m_Age;
};
void test02()
{
vector<Person> v;
Person p1("AAA",10);
Person p2("BBB",20);
Person p3("CCC",30);
Person p4("DDD",40);
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
v.push_back(p4);
Person p("CCC",30);
vector<Person>::iterator pos = find(v.begin(),v.end(),p);
if(pos==v.end())
{
cout << "未找到!" << endl;
}
else
{
cout << "找到了和p年龄和姓名都相同的人" << endl;
}
}
int main()
{
test01();
test02();
system("pause");
return 0;
}
需要注意的是,对自定义数据类型进行查找的时候,一定要在自定义数据类型中重载==
操作符,不然find
算法底层实现知道如何判断自定义数据类型是否相等。
5.2.2 find_if
find_if(iterator beg,iterator end,_Pred)
_Pred
为函数或者谓词(返回值为bool
的仿函数)。
find_if
的返回值是迭代器。下面是底层实现代码:
(核心是for
循环部分)
从定义可以看出是遍历容器中每个元素,将每个元素值传给谓词,然后进行判断。如果条件一直没有满足,就会返回end迭代器,找到了就直接返回第一个找到的符合条件的元素的迭代器。
示例:
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<string>
// find_if 内置数据类型
class GreaterFive
{
public:
bool operator()(int val)
{
return val > 5;
}
};
void test01()
{
vector<int> v;
for(int i = 0;i<10;i++)
{
v.push_back(i);
}
vector<int>::iterator pos = find_if(v.begin(),v.end(),GreaterFive());
if(pos == v.end())
{
cout << "未找到!" << endl;
}
else
{
cout << "找到了!" << endl;
}
}
// find_if 自定义数据类型
class Person
{
public:
Person(string name,int age)
{
this->m_Name =name;
this->m_Age = age;
}
string m_Name;
int m_Age;
};
class AgeGreater20
{
public:
bool operator()(Person &p)
{
return p.m_Age > 20;
}
};
void test02()
{
vector<Person> v;
Person p1("AAA",10);
Person p2("BBB",20);
Person p3("CCC",30);
Person p4("DDD",40);
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
v.push_back(p4);
vector<Person>::iterator pos = find_if(v.begin(),v.end(),AgeGreater20());
if(pos == v.end())
{
cout << "未找到!" << endl;
}
else
cout << "找到了年龄为" << pos->m_Age << "的" << pos->m_Name << endl;
}
int main()
{
cout << "test01" << endl;
test01();
cout << "=================" << endl;
cout << "test02" << endl;
test02();
system("pause");
return 0;
}
5.2.3 adjacent_find
查找相邻重复元素,如果查到,返回相邻元素的第一个位置的迭代器。
函数原型:
adjacent_find(iterator beg,iterator end);
示例:
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
void test01()
{
vector<int> v;
v.push_back(0);
v.push_back(2);
v.push_back(0);
v.push_back(2);
v.push_back(1);
v.push_back(3);
v.push_back(3);
vector<int>::iterator pos = adjacent_find(v.begin(),v.end());
if(pos == v.end())
cout << "未找到!"<< endl;
else
cout << *pos <<" "<< *(++pos) << endl;
// 找到就输出相邻位置的元素,看一看是否真的相等
}
int main()
{
test01();
system("pause");
return 0;
}
5.2.4 binary_search
二分查找法,查找指定元素value,找到返回true,未找到返回false。虽然速度快,但是在无序序列中不可用。
函数原型:
bool binary_search(iterator beg, iterator end,value);
示例:
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
void test01()
{
vector<int> v;
for(int i=0;i<10;i++)
{
v.push_back(i);
}
//v.push_back(2);
// 如果容器是无序的,则查找结果是未知的
bool ret = binary_search(v.begin(),v.end(),9);
if(ret)
cout << "找到了"<< endl;
else
cout << "未找到!"<< endl;
}
int main()
{
test01();
system("pause");
return 0;
}
5.2.5 count
统计元素value出现的次数,返回值是int
类型。
函数原型:
count(iterator beg, iterator end, value);
示例:
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
// 统计内置数据类型
void test01()
{
vector<int> v;
v.push_back(10);
v.push_back(20);
v.push_back(10);
v.push_back(20);
v.push_back(10);
int num = count(v.begin(),v.end(),20);
cout << "容器中20的个数为"<< num << endl;
}
// 统计自定义数据类型
class Person
{
public:
Person(string name,int age)
{
this->m_Name = name;
this->m_Age = age;
}
bool operator==(const Person &p)
{
if(this->m_Age==p.m_Age)
return true;
else
return false;
}
string m_Name;
int m_Age;
};
void test02()
{
vector<Person> v;
Person p1("刘备",30);
Person p2("关羽",30);
Person p3("张飞",30);
Person p4("赵云",20);
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
v.push_back(p4);
Person p5("孔明",30);
// 查找和孔明年龄相同的人的个数
int num = count(v.begin(),v.end(),p5);
cout <<"和孔明年龄相同的人个数为"<< num << endl;
}
int main()
{
test01();
test02();
system("pause");
return 0;
}
在底层实现上, 其实就是遍历将每个数据和传入的数据相比,将比较结果作为if
的条件,如果结果为true
,则执行index++
的操作,遍历所有元素之后,就可以统计出相同的人数。但是对于自定义数据类型,底层代码并不知道如果判断是否相等,所以需要重载==
,而且返回值为bool
类型。
5.2.6 count_if
按条件统计元素个数
count_if (iterator beg, iterator end, _Pred)
_Pred
是谓词,用于统计条件的实现。
示例:
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
// count_if 内置数据类型
class Greater5
{
public:
bool operator()(int val)
{
return val > 5;
}
};
void test01()
{
vector<int> v;
for(int i =0;i<10;i++)
{
v.push_back(i);
}
// 查找大于5的数据的个数
int num = count_if(v.begin(),v.end(),Greater5());
cout << "容器中大于5的元素个数为" << num << endl;
}
// find_if 自定义数据类型
class Person
{
public:
Person(string name,int age)
{
this->m_Name = name;
this->m_Age = age;
}
string m_Name;
int m_Age;
};
class less25
{
public:
bool operator()(const Person &p)
{
return p.m_Age < 25;
}
};
void test02()
{
vector<Person> v;
Person p1("刘备",30);
Person p2("关羽",26);
Person p3("张飞",24);
Person p4("赵云",20);
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
v.push_back(p4);
// 统计年龄小于25的人数
int num = count_if(v.begin(),v.end(),less25());
cout << "容器中年龄小于25的人数为" << num << endl;
}
int main()
{
test01();
test02();
system("pause");
return 0;
}
执行结果:
5.3 常用排序算法
算法简介:
sort // 对容器内元素进行排序
random_shuffle // 洗牌,指定范围内的元素随机调整次序
merge // 容器元素合并,并存储到另一容器中
reverse // 反转指定范围的元素
5.3.1 sort 和random_shuffle
函数原型:
sort(iterator beg, iterator end, _Pred)
random_shuffle(iterator bed, iterator end);
对于sort
而言,谓词可有可无,在没有谓词的时候,按照默认的升序进行排列,有谓词就按照谓词中的规则。对于随机打乱算法,为了每次打乱的结果不相同,需要加入随机数种子srand ( )
。
示例:
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<functional>
#include<ctime>
// for_ecah 普通输出函数
void myPrint(int val)
{
cout << val << " ";
}
void test01()
{
vector<int> v;
v.push_back(30);
v.push_back(50);
v.push_back(20);
v.push_back(40);
v.push_back(10);
cout << "排序前:" << endl;
// for_each 遍历输出
for_each(v.begin(),v.end(),myPrint);
cout << endl;
// sort 默认排序
cout << "sort默认排序后:" << endl;
sort(v.begin(),v.end());
for_each(v.begin(),v.end(),myPrint);
cout << endl;
// 改变sort排序规则
cout << "sort降序:" << endl;
sort(v.begin(),v.end(),greater<int>());
for_each(v.begin(),v.end(),myPrint);
cout << endl;
// random_shuffle 打乱
cout << "random_shuffle第一次打乱后:" << endl;
random_shuffle(v.begin(),v.end());
for_each(v.begin(),v.end(),myPrint);
cout << endl;
// random_shuffle 打乱
cout << "random_shuffle第二次打乱后:" << endl;
random_shuffle(v.begin(),v.end());
for_each(v.begin(),v.end(),myPrint);
cout << endl;
}
int main()
{
srand((unsigned int)time(NULL)); // 随机种子,每次打乱都不一样
test01();
system("pause");
return 0;
}
执行结果:
5.3.2 merge
将两个容器的元素合并,并存储到另一个容器中
merge(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator dest)
dest
是目标容器(即存放结果的容器)的开始迭代器。
合并前的两个容器必须是有序的,而且同为升序或降序,合并之后的容器中的元素也是有序的。
示例:
#include<iostream>
using namespace std;
#include<algorithm>
#include<vector>
void myPrint(int val)
{
cout << val << " ";
}
void Print(const vector<int> &v)
{
for_each(v.begin(),v.end(),myPrint);
cout << endl;
}
void test01()
{
vector<int> v1;
vector<int> v2;
for(int i = 0;i<5;i++)
{
v1.push_back(2*i+1);
v2.push_back(2*i);
}
cout << "v1:" << endl;
Print(v1);
cout << "v2:" << endl;
Print(v2);
cout << "v1和v2合并后:" << endl;
vector<int> v3;
v3.resize(v1.size()+v2.size()); // 提前开辟空间!!!
merge(v1.begin(),v1.end(),v2.begin(),v2.end(),v3.begin());
Print(v3);
}
int main()
{
test01();
system("pause");
return 0;
}
运行结果:
5.3.3 reverse
将容器中指定范围内元素反转
(用的不多,但是要知道这个东西的存在)
reverse(iterator beg, iterator end);
示例:
#include<iostream>
using namespace std;
#include<algorithm>
#include<vector>
void myPrint(int val)
{
cout << val << " ";
}
void Print(const vector<int> &v)
{
for_each(v.begin(),v.end(),myPrint);
cout << endl;
}
void test01()
{
vector<int> v;
for(int i = 0;i<5;i++)
{
v.push_back(2*i+1);
}
cout << "v:" << endl;
Print(v);
cout << "v反转后:" << endl;
reverse(v.begin(),v.end());
Print(v);
}
int main()
{
test01();
system("pause");
return 0;
}
运行结果:
5.4 常用拷贝和替换算法
copy // 容器内指定范围内的元素拷贝到另一个容器中
replace // 容器内指定范围的旧元素修改为新元素
replace_if // 容器内指定范围满足条件的元素替换为新元素
swap // 互换两个人容器的元素
5.4.1 copy
容器内指定范围内的元素拷贝到另一个容器中。(其实直接用赋值操作就可以实现)这里需要注意的是提前开辟内存空间。
copy(iterator beg,iterator end,iterator dest);
示例:
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
void myPrint(int val)
{
cout << val << " ";
}
void test01()
{
vector<int> v1;
for(int i = 0;i<10;i++)
{
v1.push_back(i);
}
vector<int> v2;
v2.resize(v1.size()); // 提前给空间!!!
copy(v1.begin(),v1.end(),v2.begin());
// 其实直接用赋值操作就可以实现了
for_each(v2.begin(),v2.end(),myPrint);
cout << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
5.4.2 replace和replace_if
replace(iterator beg, iterator end,oldvalue,newvalue);
// 是把区间内的所有的old value替换为new value,不是只替换第一个。
replace_if(iterator beg, iterator end,_Pred,newvalue);
// 区间内所有满足_Pred中条件的值都会被替换为new value。
示例:
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<functional>
// 仿函数实现打印输出
class myPrint
{
public:
void operator()(int val)
{
cout << val << " ";
}
};
// 谓词实现条件替换
class less_equal30
{
public:
bool operator()(int val)
{
return val <= 30;
}
};
void test01()
{
vector<int> v;
v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(10);
v.push_back(10);
v.push_back(20);
for_each(v.begin(),v.end(),myPrint());
cout << endl;
// 将10替换为100
cout << "将10替换为100:" << endl;
replace(v.begin(),v.end(),10,100);
for_each(v.begin(),v.end(),myPrint());
cout << endl;
// 将小于100的元素替换为0
cout << "将<=30的替换为0:" << endl;
replace_if(v.begin(),v.end(),less_equal30(),0);
for_each(v.begin(),v.end(),myPrint());
cout << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
运行结果:
5.4.3 swap
swap(container c1, container c2);
互换容器c1和容器c2中的内容,需要确保c1和c2是同类型的容器。
5.5 常用的算术生成算法
属于小型算法,使用时包含头文件#include <numeric>
accumulate // 计算容器元素累计总和
fill // 向容器中添加元素
5.1 accumulate
计算区间内容器元素累计总和
accumulate(iterator beg, iterator end,value)
value是起始累加值,如果不需要设置为0即可。
示例:
#include<iostream>
using namespace std;
#include<vector>
#include<numeric>
void test01()
{
vector<int> v;
for(int i = 1;i<=100;i++)
{
v.push_back(i);
}
int ret = accumulate(v.begin(),v.end(),1000);
cout << ret << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
5.5.2 fill
//fill(iterator begin, iterator end, value)
#include<iostream>
using namespace std;
#include<vector>
#include<numeric>
#include<algorithm>
void myPrint(int val)
{
cout << val << " ";
}
void test01()
{
vector<int> v;
v.resize(10); // 默认填充10个0
// 后期填充
fill(v.begin(),v.end(),100);
for_each(v.begin(),v.end(),myPrint);
cout << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
5.6 常用集合算法
set_intersection // 求两个容器的交集
set_union // 求两个容器的并集
set_difference // 求两个容器的差集
5.6.1 set_intersection
set_intersection(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator dest)
返回值是目标容器(dest)中交集最后一个元素的位置。这个位置并不是容器的结束位置,因为交集中元素个数一开始是未知的,而容器空间给的是可能的最大值,所以在遍历输出的时候需要注意用的迭代器是该算法的返回值,而不知end
迭代器。如果用end
迭代器,会输出容器中的全部元素,交集没有填满容器,就会输出0。
此外,求交集的两个集合必须是有序序列。
5.6.2 set_union
set_union(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator dest)
同样,返回值是并集最后一个元素在容器中的位置(迭代器),求交集的两个结合也必须是有序序列。
5.6.3 set_difference
set_difference(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator dest)
v1和v2的差集:v1容器中不是v1和v2交集的部分
v2和v1的差集:v2容器中不是v1和v2交集的部分
示例:
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
void myPrint(int val)
{
cout << val << " ";
}
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+5); // 5-14
}
cout << "v1:" ;
for_each(v1.begin(),v1.end(),myPrint);
cout << endl;
cout << "v2:";
for_each(v2.begin(),v2.end(),myPrint);
cout << endl;
vector<int> vTarget;
// 求交集
// 1. 提前开辟空间,要开辟多大?
// 交集最多是v1和v2元素个数的最小值
vTarget.resize(min(v1.size(),v2.size()));
// 2. 获取交集
vector<int>::iterator pos = set_intersection(v1.begin(),v1.end(),v2.begin(),v2.end(),vTarget.begin());
cout << "交集为:" << endl;
for_each(vTarget.begin(),pos,myPrint);
cout << endl;
// 求并集
vTarget.resize(max(v1.size(),v2.size()));
pos = set_union(v1.begin(),v1.end(),v2.begin(),v2.end(),vTarget.begin());
cout << "并集为:" << endl;
for_each(vTarget.begin(),pos,myPrint);
cout << endl;
// 求差集 v1和v2
vTarget.resize(max(v1.size(),v2.size()));
pos = set_difference(v1.begin(),v1.end(),v2.begin(),v2.end(),vTarget.begin());
cout << "v1和v2的差集为:" << endl;
for_each(vTarget.begin(),pos,myPrint);
cout << endl;
// 求差集 v2和v1
pos = set_difference(v2.begin(),v2.end(),v1.begin(),v1.end(),vTarget.begin());
cout << "v2和v1的差集为:" << endl;
for_each(vTarget.begin(),pos,myPrint);
cout << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
运行结果: