大家好,欢迎来到 STL【常用算法】详解模块,本文将对 STL 中常见但又高频的算法进行一个详细又简单的入门级教学:mortar_board:
@TOC
一、算法概述
1.什么是算法?
算法就是一种函数模板,C++中的算法是通过迭代器和模板来实现的,简单来说算法就是一系列的计算步骤,用来将输入数据转化成输出结果。
2.算法是怎么样生成的?
对于算法,它是从迭代器哪里获得一个元素,而迭代器则查找元素的位置并将这些信息提供给算法能够访问这些元素。
3.算法的头文件有哪些?①这是 STL 中最大的一个头文件,范围设计到比较、交换、查找、遍历操作、复制、修改等等
②这个算法头文件它的体积很小,只包括几个在序列上面进行简单数学运算的模板函数
③这个头文件它主要是定义了一些模板类,用以声明函数对象
4.算法可以分为哪几大类?:tiger:
二、常用算法合集
1、遍历算法
1.1 for_each(遍历容器)
①算法简介
②函数原型
for_each(iterator beg,iterator end,_func);
//beg - 开始迭代器
//end- 结束迭代器
//-func - 函数或函数对象
复制代码
③示例代码
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
//常用遍历算法 - for_each
//普通函数
void print1(int val)
{
cout << val << " ";
}
//仿函数
class print2 {
public:
void operator()(int val)
{
cout << val << " ";
}
};
void test1()
{
vector<int> v;
for (int i = 0; i < 10; ++i)
{
v.push_back(i);
}
for_each(v.begin(), v.end(), print1);
cout << endl;
for_each(v.begin(), v.end(), print2());
cout << endl;
}
int main(void)
{
test1();
return 0;
}
复制代码
运行结果:
讲解:
1.2 transform(搬运容器)
①算法简介
transform(iterator beg1,iterator end1,iterator beg2,_func);
//beg1 - 原容器的开始迭代器
//end1- 原容器的结束迭代器
//beg2 - 目标容器的开始迭代器
//_func - 函数或函数对象
复制代码
③示例代码
class Transform {
public:
int operator()(int v)
{
return v + 100;
}
};
//输出
class print{
public:
void operator()(int val)
{
cout << val << " ";
}
};
void test2()
{
vector<int> v;
for (int i = 0; i < 10; ++i)
{
v.push_back(i);
}
vector<int> v2;
v2.resize(v.size()); //以v1的大小为v2重新开辟空间
transform(v.begin(),v.end(),v2.begin(),Transform());
for_each(v2.begin(), v2.end(), print());
}
复制代码
运算结果:
讲解:
2、查找算法
2.1 find(查找元素)
①算法简介
②函数原型
find(iterator beg,iterator end,value);
//beg - 开始迭代器
//end- 结束迭代器
//value- 查找的元素
复制代码
③示例代码首先是==内置数据类型==的查找
void test3()
{
vector<int> v;
for (int i = 0; i < 10; ++i)
{
v.push_back(i);
}
vector<int>::iterator it = find(v.begin(), v.end(), 5);
if (it == v.end())
{
cout << "没有找到此元素" << endl;
}
else
{
cout << "找到了," << "此元素为:" << *it << 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_name == p.m_name && this->m_age == p.m_age)
{
return true;
}
else
{
return false;
}
}
string m_name;
int m_age;
};
void test4()
{
vector<Person> v2;
Person p1("aaa", 10);
Person p2("bbb", 20);
Person p3("ccc", 30);
Person p4("ddd", 40);
v2.push_back(p1);
v2.push_back(p2);
v2.push_back(p3);
v2.push_back(p4);
Person pp("aaa", 10);
vector<Person>::iterator it = find(v2.begin(), v2.end(), pp);
if (it == v2.end())
{
cout << "没有找到此元素" << endl;
}
else
{
cout << "找到了," << "姓名为:" << it->m_name << "\t年龄为:" << it->m_age << endl;
}
}
复制代码
运行结果:
讲解:
template <class _InIt, class _Ty>
_NODISCARD _CONSTEXPR20 _InIt find(_InIt _First, const _InIt _Last, const _Ty& _Val) { // find first matching _Val
_Adl_verify_range(_First, _Last);
_Seek_wrapped(_First, _Find_unchecked(_Get_unwrapped(_First), _Get_unwrapped(_Last), _Val));
return _First;
}
复制代码
我们到 find 算法的底层可以知晓,,因为我们现在操作的是自定义的数据类型,编译器并不知道如何去做"=="号的对比,不知道是比较姓名还是年龄,因此我们需要利用 C++中重载运算符的操作,将这个等于号重新定义一下,于是就有了这段代码,在这样重载后,进行两个元素的比较就不会出现问题了。所以大家在操纵自定义数据类型的时候别忘了重载这个等于号;
bool operator==(const Person& p)
{
if (this->m_name == p.m_name && this->m_age == p.m_age)
{
return true;
}
else
{
return false;
}
}
复制代码
2.2 find_if(按条件查找元素)
①算法简介
②函数原型
transform(iterator beg1,iterator end1,iterator beg2,_func);
//beg1 - 开始迭代器
//end1- 结束迭代器
//_Pred - 函数或者谓词(返回布尔类型的仿函数)
复制代码
③示例代码
由于这里的代码与 find 中相似,只是做了一些仿函数的修改,因此只出示关键代码
//内置数据类型
class GreaterFive {
//仿函数的编写
public:
bool operator()(int v)
{
return v > 5;
}
};
//具体函数的使用
vector<int>::iterator it = find_if(v.begin(), v.end(), GreaterFive());
复制代码
//自定义数据类型
class Greater20 {
//仿函数的编写
public:
bool operator()(Person& p)
{
return p.m_age > 20;
}
};
//具体函数的使用
vector<Person>::iterator it = find_if(v2.begin(), v2.end(), Greater20());
复制代码
运行结果:
讲解:这里是使用到了谓词,它是仿函数的一种,我们把返回 bool 类型的仿函数就叫做谓词。首先对于内置数据类型,return v > 5 是返回一个大于 5 的数字,如果找到了,它会返回 true,如果没找到呢,就会返回 false;然后是对于自定义数据类型,return p.m_age > 20 是返回年龄大于 20 岁的所属元素对象,在项目制作时,仿函数还是会起到很大作用的,大家可以去了解一下。
2.3 adjacent_find(查找相邻重复元素)
①算法简介
②函数原型
adjacent_find(iterator beg,iterator end);
//beg - 开始迭代器
//end- 结束迭代器
复制代码
③示例代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
//常用查找算法 - adjacent_find
void test7()
{
vector<int> v;
v.push_back(4);
v.push_back(1);
v.push_back(6);
v.push_back(9);
v.push_back(9);
v.push_back(14);
v.push_back(2);
vector<int>::iterator it=adjacent_find(v.begin(), v.end());
if (it == v.end())
cout << "没找到相邻的重复元素" << endl;
else
cout << "找到相邻的重复元素,为:" << *it << endl;
}
int main(void)
{
test7();
return 0;
}
复制代码
运行结果:
讲解:对于这个算法,它并不复杂,传入需查找元素的其实迭代器和结束迭代器就行,我们来看一下它的底层定义,从中可以看出,它是利用了一个 next 值去不断更新起始迭代器的位置,找出重复的元素,然后去逐一判断两个元素是否相邻,如果 next != last,则表示此时后面的元素与前一个元素已然不同,因此跳出 while 循环,此时的后一位元素。
从这里我们可以看出这个算法的调用虽然很简单,但是其底层原理的判断还是有些曲折,但好在是有这么一个算法,如果我们将它记住,在需要用到此功能的场景时就可以得心应手了:sheep:
template <class ForwardIterator>
ForwardIterator adjacent_find (ForwardIterator first, ForwardIterator last)
{
if (first != last)
{
ForwardIterator next=first; ++next;
while (next != last) {
if (*first == *next) // or: if (pred(*first,*next)), for version (2)
return first;
++first; ++next;
}
}
return last;
}
复制代码
2.4 binary_search(二分查找法)
①算法简介
②函数原型
bool binary_search(iterator beg,iterator end,value);
//beg - 开始迭代器
//end- 结束迭代器
//value - 查找的元素
复制代码
③示例代码
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;
复制代码
讲解:由于其运行结果过于简洁明了,就不作展示了,从代码中可以看出,这个算法和前面的算法不太一样,因为它要判断是否找到需查元素,因此会返回 true 和 false,因此就要定义一个布尔类型的值去接收它。这里还要注意的一点是,查找的序列要为一个有序序列,不可是无序序列,但如果大家在做题的时候碰到的是一个无序序列,可以用归并排个序,然后用此二分查找,在我认为这样的时间复杂度是最低的,因为归并较其他的排序来说相对稳定,而且时间复杂度也比较低,大家可以去学习一下,它是十大排序算法中的一种,这里给出表格大家可以先参照一下:snail:;
2.5 count(统计元素个数)
①算法简介
②函数原型
count(iterator beg,iterator end,value);
//beg - 开始迭代器
//end- 结束迭代器
//value - 查找的元素
复制代码
③示例代码
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
//常用查找算法 - count
//内置数据类型
void test9()
{
vector<int> v;
v.push_back(80);
v.push_back(10);
v.push_back(50);
v.push_back(80);
v.push_back(80);
v.push_back(80);
int num = count(v.begin(), v.end(), 80);
cout << "容器中数值 80 有:" << 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 test10()
{
vector<Person> v;
Person p1("刘备",35);
Person p2("关羽",40);
Person p3("张飞",36);
Person p4("孙权",35);
Person p5("孙策",35);
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
v.push_back(p4);
v.push_back(p5);
Person p("诸葛亮", 35);
int num = count(v.begin(), v.end(), p);
cout << "容器中与诸葛亮年龄相同的人有:" << num << "个" << endl;
}
/**/int main(void)
{
test9();
cout << endl;
test10();
return 0;
}
复制代码
运行结果:
讲解:本算法也是可以有两种数据类型的统计,其元素和上述讲过的算法类似,故不做过多分析,大家一看就能懂,就是在统计容器中指定元素的个数
2.6 count_if(按条件统计元素个数)
①算法简介
②函数原型
count_if(iterator beg,iterator end,value);
//beg - 开始迭代器
//end- 结束迭代器
//value - 查找的元素
复制代码
③示例代码这里一样只给出关键代码
//内置数据类型
class Greater20 {
public:
int operator()(int v)
{
return v > 20;
}
};
复制代码
class Greater30 {
public:
int operator()(const Person& p)
{
return p.m_age > 35;
}
};
复制代码
运行结果:
3、排序算法
3.1 sort(快速排序)
①算法简介
②函数原型
sort(iterator beg,iterator end,_Pred);
//beg - 开始迭代器
//end- 结束迭代器
//_Pred - 谓词
复制代码
③示例代码
class print {
public:
void operator()(int val)
{
cout << val <<" ";
}
};
void test1()
{
vector<int> v;
v.push_back(10);
v.push_back(90);
v.push_back(40);
v.push_back(60);
v.push_back(70);
sort(v.begin(), v.end()); //默认升序 - less
for_each(v.begin(), v.end(), print());
cout << endl;
sort(v.begin(), v.end(),greater<int>()); //降序 - greater
for_each(v.begin(), v.end(), print());
cout << endl;
}
复制代码
运行结果:
讲解:从运行结果我们可以看出,对容器中的无序数据进行默认 sort()排序,是升序的,谓词也就是 less,这个和优先队列里面的小顶堆类似,然后我们去更改它的谓词值,改为 greater(),看显示结果就变为了降序,greater 也就是大顶锥,在优先队列中相当于优先出队中最大的元素,所以最前面的就是最大的,了解了这两种谓词,那你也就掌握快速的使用方法;
3.2 random_shuffle(随机调整次序)
①算法简介
②函数原型
transform(iterator beg,iterator end);
//beg - 开始迭代器
//end- 结束迭代器
复制代码
③示例代码
class print {
public:
void operator()(int val)
{
cout << val << " ";
}
};
void test2()
{
srand((unsigned int)time(NULL));
vector<int> v;
for (int i = 0; i < 10; ++i)
{
v.push_back(i);
}
cout << "打乱前" << endl;
for_each(v.begin(), v.end(), print());
cout << endl;
cout << "打乱后" << endl;
random_shuffle(v.begin(), v.end());
for_each(v.begin(), v.end(), print());
cout << endl;
}
复制代码
运行结果:
讲解:从运行结果我们可以看出,确实是进行了一个洗牌的操作,对 vector 容器,我们插入了十个 0-9 之间的数,在传入其 begin()和 end()直接便直接进行“洗牌”操作,这个的话在生活实例中可以做类似于抽签之类的操作,可以打乱原数组的顺序,既然有这么一个现成的算法,我们就可以直接拿来用,不需要在自己重新写一个了
3.3 merge(容器元素合并)
①算法简介
②函数原型
merge(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator dest);
//beg1 - 容器1的开始迭代器
//end1- 容器1的结束迭代器
//beg2 - 容器2的开始迭代器
//end2- 容器2的结束迭代器
//dest - 目标容器的起始迭代器
复制代码
③示例代码
vector<int> v1;
vector<int> v2;
v1.push_back(2);
v1.push_back(5);
v1.push_back(6);
v1.push_back(10);
v1.push_back(48);
v2.push_back(8);
v2.push_back(15);
v2.push_back(20);
v2.push_back(28);
v2.push_back(30);
cout << "v1容器" << endl;
for_each(v1.begin(), v1.end(), print());
cout << endl;
cout << "v2容器" << endl;
for_each(v2.begin(), v2.end(), print());
cout << endl;
vector<int> v3;
v3.resize(v1.size() + v2.size());
cout << "合并后的容器为:" << endl;
merge(v1.begin(), v1.end(), v2.begin(), v2.end(), v3.begin());
for_each(v3.begin(), v3.end(), print());
cout << endl;
复制代码
运行结果:
讲解:从运行结果我们可以看到首先是生成了两个容器,并且分别存入五个数据,然后就是将容器 1 的起始迭代器和结束迭代器传入,同理,容器 2 也是一样,最后放置的是目标容器的起始迭代器,但是这里有一个==细节==要注意,如果你直接将两个容器合并放到一个容器中,那这个目标容器其实是空的,没有存储空间来放置这些元素,所以这里我们要利用容器 1 和容器 2 的大小来 resize(重新开辟)目标容器的大小,其大小表示前二者之和,从结果来看,这个算法不仅可以实现容器的合并,还可以对合并后的这些数据自动进行一个排序
3.4 reverse(容器元素反转)
①算法简介
②函数原型
transform(iterator beg1,iterator end1,iterator beg2,_func);
//beg - 开始迭代器
//end- 结束迭代器
复制代码
③示例代码
v.push_back(2);
v.push_back(5);
v.push_back(6);
v.push_back(10);
v.push_back(48);
cout << "反转前" << endl;
for_each(v.begin(), v.end(), print());
cout << endl;
cout << "反转后" << endl;
reverse(v.begin(), v.end());
for_each(v.begin(), v.end(), print());
cout << endl;
复制代码
运行结果:
讲解:从代码和运行结果来看,这个算法都显得非常得简单,但正是因为它简单,所以我们更应该很好地去记住它,虽然这个功能实现起来也不太麻烦,十行代码之内也能解决,但这就是 STL 算法的魅力,可以将大家都常用的功能封装成一个算法,一个模板,可以让我们直接来使用,在进行一些大型开发时就可以很好地利用
4、拷贝和替换算法
4.1 copy(容器元素拷贝)
①算法简介
②函数原型
copy(iterator beg,iterator end,iterator dest);
//beg - 开始迭代器
//end- 结束迭代器
//dest- 目标容器其实迭代器
复制代码
③示例代码
vector<int> v;
for (int i = 0; i < 10; ++i)
{
v.push_back(i);
}
cout << "原容器中的元素为:";
for_each(v.begin(), v.end(), print);
cout << endl;
vector<int> v2;
v2.resize(v.size());
copy(v.begin(), v.end(), v2.begin());
cout << "拷贝完后的目标容器元素为:";
for_each(v2.begin(), v2.end(), print);
cout << endl;
复制代码
运行结果:
讲解:这个算法也和上一个 reverse 算法一样,都不是很难,但确实用的比较多的两个算法,这里也是要注意,对需要拷贝的目标容器,一样要重新开辟其容器空间,而且要以原容器的 size()来开辟;
接下来介绍的这两个算法和 find,find_if 以及 count,count_if 类似,因此只做简略分析,给出关键代码
4.2 replace(修改元素)
①算法简介
②函数原型
replace(iterator beg,iterator end,oldvalue,newvalue);
//beg - 开始迭代器
//end- 结束迭代器
//oldvalue - 旧元素
//newvalue - 新元素
复制代码
③示例代码
vector<int> v;
v.push_back(50);
v.push_back(10);
v.push_back(70);
v.push_back(30);
v.push_back(60);
v.push_back(20);
v.push_back(50);
cout << "替换前为:" << endl;
for_each(v.begin(), v.end(), print2);
cout << endl;
replace(v.begin(), v.end(), 50, 5000);
cout << "替换后为:" << endl;
for_each(v.begin(), v.end(), print2);
cout << endl;
复制代码
运行结果:
讲解:从运行结果可以看出,原容器中的 50 都被改成了 5000,实现了整体替换的功能
4.3 replace_if(按条件修改元素)
①算法简介
②函数原型
transform(iterator beg1,iterator end1,_pred,newvalue);
//beg - 开始迭代器
//end- 结束迭代器
//_pred - 谓词
//newvalue - 替换的新元素
复制代码
③关键代码
class Greater30 {
public:
int operator()(int val)
{
return val > 30;
}
};
复制代码
replace_if(v.begin(), v.end(), Greater30(), 3000);
复制代码
运行结果:
讲解:从运行结果我们可以看出,使用了谓词 Greater30()后,原容器中所有>30 的数都被改成了 3000,这个功能其实就挺强大的,因为我们程序员在开发的过程中,每天要面对上万行代码,对一些小数据不可能一个个地去改,但如果有了这种指定修改的功能,那么就会==事半而功倍==
4.4 swap(容器元素交换)
①算法简介
②函数原型
swap(containter c1,containter c2);
//c1 - 容器1
//c2 - 容器2
复制代码
注:传入的容器需要同种类型的,不可以是一个 vector 容器一个 list 容器③示例代码
vector<int> v1;
vector<int> v2;
for (int i = 0; i < 10; ++i)
{
v1.push_back(i);
v2.push_back(i + 100);
}
cout << "替换前为:" << endl;
for_each(v1.begin(), v1.end(), print4);
cout << endl;
for_each(v2.begin(), v2.end(), print4);
cout << endl;
cout << "替换后为:" << endl;
swap(v1, v2);
for_each(v1.begin(), v1.end(), print4);
cout << endl;
for_each(v2.begin(), v2.end(), print4);
cout << endl;
复制代码
运行结果:
讲解:从结果我们可以看到,起初两个 vector 容器 v1 和 v2 分别是 0-9 和 100-109,然后经过 swap()函数替换之后,输出的顺序就发生了改变,其实两个容器已经发生了互换,怎么样,是不是很简便,只需要传入两个容器体即可,无需传入迭代器
5、算数生成算法
接下来将两个常用的算数生成算法,这两个算法属于一种小型算法,它们所属的头文件并不是 algorithm,而是我们开头提到过的 numeric
5.1 accumulate(计算累计总和)
①算法简介
②函数原型
accumulate(iterator beg,iterator end,value);
//beg - 开始迭代器
//end- 结束迭代器
//value - 起始的累加值
复制代码
③示例代码
vector<int> v;
for (int i = 0; i <= 100; ++i)
{
v.push_back(i);
}
//参数3 - 起始的累加值
int total = accumulate(v.begin(), v.end(), 0);
cout << "total=" << total << endl;
复制代码
运行结果:
讲解:可以看出代码和运行结果都非常简单,只需要传入一个容器的起始迭代器和结束迭代器,然后最后一个参数值得关注一下,指的就是==起始的累加值==,比如说我们这里是从 0 加到 100,结果是 5050,因为起始累加值是 0,但如果将其改为 1000,那么这个结果就要在 1000 的基础上去累加,出来的结果便是 6050 了,大家明白了吗:whale:
5.2 fill(填充元素)
①算法简介
②函数原型
fill(iterator beg,iterator end,value);
//beg - 开始迭代器
//end- 结束迭代器
//value - 填充的值
复制代码
③示例代码
vector<int> v;
v.resize(10);
cout << "初始化为:" << endl;
for_each(v.begin(), v.end(), print);
cout << endl;
//后期重新填充
fill(v.begin(), v.end(), 100);
cout << "重新填充后为:" << endl;
for_each(v.begin(), v.end(), print);
cout << endl;
复制代码
运行结果:
讲解:首先我们创建了一个 vector 容器,一开始的时候我们就用 resize()的方式指定几个数字进去,这里会按照你指定的大小来给你填充默认 0,但是在后期如果我们像修改这些值,就可以使用 fill 来进行一个重新的填充。很简单,只需要传入容器的起始的结束迭代器,以及需要填充的值,观看运行结果,我们便可以看到容器中的 10 个元素已经被填充成为 0;
6、集合算法
最后呢,我们来看看常用的集合算法,主要有交集、并集和差集这几种,为了可视化,所以配了插图哦:milky_way:
6.1 set_intersection(交集)
①算法简介
②函数原型
set_intersection(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator dest);
//beg1 - 容器1开始迭代器
//end1 - 容器1结束迭代器
//beg2 - 容器2开始迭代器
//end2 - 容器2结束迭代器
//dest - 目标容器开始迭代器
复制代码
③示例代码
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
} //--》5 6 7 8 9
vector<int> v3;
//目标容器需要提前开辟空间
//最特殊情况 大容器包含小容器 开辟空间 取小容器的size即可
v3.resize(min(v1.size(), v2.size())); //min() - 比较两者的大小
vector<int>::iterator itEnd = set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(), v3.begin());
cout << "两容器中元素的交集为:";
//返回目标容器最后一个元素的迭代器位置
for_each(v3.begin(), itEnd, print());
//for_each(v3.begin(), v3.end(), print()); //相当于把容器所有所有数据都遍历一遍
//如果用的不是itEnd而是v3.end(),
复制代码
运行结果:
讲解:运行结果很明确,和我们前面分析的一致,这里要注意的是,对于目标容器,我们要重新开辟其大小,以===最坏的情况==来分析,就是大容器包含小容器,因此我们将目标容器的大小设置为两个容器中较小的那个便可,这里的 min()函数就是取两个当中最小值,然后因为 set_intersection()返回的是一个迭代器,所以我们要用对应的迭代器去接收,最后要注意的一点是在遍历输出的时候,结束迭代器的位置不可以写当前迭代器的 end(),不然就会出现下面这样的情况 :point_down:
这样相当于把容器中所有数据都遍历一遍,但是刚才我们在开辟容器的时候,我们考虑的时候一种最特殊的情况,但实际中可以比这个大,也有可能比这个小,这里就是比大了,所以容器中剩余的部分自动填充为 0,但我们用这个求交集算法返回的迭代器的位置,就是它最后所求得的两个容器中相同的最后一个元素,所以不会出现多余的情况
6.2 set_union(并集)
①算法简介
②函数原型
set_union(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator dest);
//beg1 - 容器1开始迭代器
//end1 - 容器1结束迭代器
//beg2 - 容器2开始迭代器
//end2 - 容器2结束迭代器
//dest - 目标容器开始迭代器
复制代码
③关键代码
v3.resize(v1.size() + v2.size());
vector<int>::iterator itEnd = set_union(v1.begin(), v1.end(), v2.begin(), v2.end(), v3.begin());
for_each(v3.begin(), itEnd, print());
复制代码
运行结果:
讲解:这里的并集和求交集的代码很是类似,因此只给出关键代码,依旧要注意的一点是,这里目标容器的大小也是要重新开辟,这里要考虑到的最坏情况就是两个容器无交集,是分离的,就像我上面画的那样,所以大小等于的他们的 size()相加。还有一点也是一样,就是在输出的时候要用 set_union()算法最后遍历到的迭代器,不要用我们自己开辟大小的迭代器的末尾元素,不然会像求交集那样后面输出都是 0
6.2 set_difference(差集)
①算法简介
②函数原型
set_difference(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator dest);
//beg1 - 容器1开始迭代器
//end1 - 容器1结束迭代器
//beg2 - 容器2开始迭代器
//end2 - 容器2结束迭代器
//dest - 目标容器开始迭代器
复制代码
③关键代码
vector<int> v3;
//最特殊的情况,两个容器没有交集,差集便是大的那个容器
v3.resize(max(v1.size(), v2.size()));
cout << "v1与v2的差集为:" << endl;
vector<int>::iterator itEnd=set_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), v3.begin());
for_each(v3.begin(), itEnd, print);
cout <<endl<< "v2与v1的差集为:" << endl;
itEnd = set_difference(v2.begin(), v2.end(), v1.begin(), v1.end(), v3.begin());
for_each(v3.begin(), itEnd, print);
复制代码
运行结果:
讲解:从运行结果来看,对于我们的原理图是不是更加清晰了,好了,这就是求两个容器的差集。这三个对于集合方面的运算其实在企业开发过程中还是可能能用到,因为可以对两个容器中的数据进行求解多数求解操作,大家能记住的话还是很有帮助的
三、总结与回顾
完结撒花:hibiscus: 看完了 STL 中的这些常用算法,你对于算法有没有一个基本的概念呢,让我们一起来回顾一下吧,在本文中,我们分别讲了==遍历==、==查找==、==排序==、==拷贝==、==替换==、==算法生成==和==集合算法==这些,每一种算法里面分别例举几个常见的,但是 STL 中的算法可不止这些,还有很多呢,因为只是给大家做一个了解入门,所以只例举了里面的一些常见但高频的算法,如果您还想继续学习一些更高级的算法,像 equal_range(折半查找)、partial_sort(局部排序)以及 pre/next_permutation(上\下一个排列组合)这些等等。推荐大家去看《STL 源码剖析》这本书,里面介绍很多算法以及计算机内部的一些底层实现
最后感谢您对本文的观看,如有问题,请于评论区或私信指出,谢谢
评论