【C++】黑马C++泛型编程和STL技术 (11) STL常用算法

算法主要由头文件<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);

begend的范围内查找指定元素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;
}

运行结果:
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值