【C++21天养成计划】STL算法超全整理!(Day18)

   日期:2020-10-06     浏览:107    评论:0    
核心提示:横扫STL算法,帅翻全场!

大家好!我是【AI 菌】,一枚不熬夜的程序员。我热爱AI、热爱分享、热爱开源! 这博客是我对学习的一点总结与思考。如果您也对 深度学习、机器视觉、算法、Python、C++ 感兴趣,可以关注我的动态,我们一起学习,一起进步~
我的博客地址为:【AI 菌】的博客
我的Github项目地址是:【AI 菌】的Github

文章目录

    • 一、什么是STL算法
    • 二、常用的STL算法汇总
      • 2.1 非变序算法
      • 2.2 变序算法
    • 三、STL 常用算法示例
      • 3.1 根据值或条件查找元素find()
      • 3.2 计算包含给定值或满足给定条件的元素数count()
      • 3.3 在集合中搜索元素或序列search()
      • 3.4 将容器中的元素初始化为指定值fill()
    • 四、STL 进阶算法示例
      • 4.1 复制操作copy()
      • 4.2 删除操作erase()
      • 4.3 替换操作replace()
      • 4.4 排序操作sort()
      • 4.5 删除重复元素操作unique()
      • 4.6 插入元素操作insert()
    • 养成习惯,先赞后看!你的支持是我创作的最大动力!

一、什么是STL算法

所谓STL,就是标准模板库。标准模板库为我们提供了易于使用的容器,比如我们前面已经学习过的 list、stack、queue、vector、string、set 和 map 类都属于STL。

STL 还为我们提供了一部分易于使用的通用函数,这些函数位于头文件 < a l g o r i t h m > <algorithm> <algorithm>中,它和容器类的成员函数一样,能高效地帮助我们操作容器中的内容。


在程序设计过程中,经常会用法查找、搜索、删除和计数等算法。C++中,STL通过通用的模板函数提供了这些算法和其他的很多算法,可通过迭代器对容器进行操作。这些通用的模板函数就是所说的STL算法。在使用STL算法之前,必须在程序开头加上头文件:

#include <algorithm>

二、常用的STL算法汇总

STL算法可以分为两大类:非变序算法变序算法

2.1 非变序算法

所谓非变序算法就是:不改变容器中元素的顺序内容的算法。

下面总结一些常用的非变序算法:

(1) 计数算法

算法 描述
count 在指定范围内,查找值与指定值匹配的所有元素
count_if 在指定范围内,查找值满足指定条件的所有元素

(2) 搜索算法

算法 描述
search 在目标范围内,根据元素相等性(或指定二元谓词)搜索第一个满足条件的元素
search_n 在目标范围内,搜索与指定值相等或满足指定谓词的n个元素
find 在给定范围内,搜索与指定值匹配的第一个元素
find_if 在给定范围内,搜索满足指定条件的第一个元素
find_end 在指定范围内,搜索最后一个满足特定条件的元素
find_first_of 在目标范围内,搜索指定序列中的任何一个元素第一次出现的位置;在另一个重载版本中,它搜索满足指定条件的第一个元素
adjacent_find 在集合中搜索两个相等或满足指定条件的元素

(3) 比较算法

算法 描述
equal 比较两个元素是否相等或使用指定的二元谓词判断连两者是否相等
mismatch 使用指定的二元谓词找出两个元素范围的第一个不同的地方

2.2 变序算法

变序算法改变其操作的序列的元素或者内容,下面我将列出一些最常用的变序算法:

(1) 初始化算法

算法 描述
fill 将指定值分配给,指定范围中的每个元素
fill_n 将指定值分配给,指定范围中的前n个每个元素

(2) 修改算法

算法 描述
for_each 对指定范围内的每个元素,执行指定的操作
transform 对指定范围内的每个元素,执行指定的一元函数

(3) 复制算法

算法 描述
copy 将一个范围复制到另一个范围
copy_backward 将一个范围复制到另一个范围,但在目标范围中将元素的排列顺序反转

(4) 删除算法

算法 描述
remove 将指定范围中包含指定的值删除
remove_if 将指定范围中满足指定一元谓词的元素删除
remove_copy 将源范围中除包含指定值外的所有元素复制到目标范围
remove_copy_if 将源范围中除满足指定一元谓词外的所有元素复制到目标范围
unique 比较指定范围内的相邻元素,并删除重复的元素
unique_copy 将源范围内的所有元素复制到目标范围,但相邻的重复元素除外

(5) 替换算法

算法 描述
replace 用一个值来替换指定范围中与指定值匹配的所有元素
replace_if 用一个值来替换指定范围中满足指定条件的所有元素

(6) 排序算法

算法 描述
sort 使用指定的排序标准,对指定范围内的元素进行排序,排序标准由二元谓词提供
stable_sort 类似于sort,但在排序时保持相对顺序不变
partial_sort 将源范围内指定数量的元素排序
partial_sort_copy 将源范围内的元素复制到目标范围,同时对它们进行排序

(7) 分区算法

算法 描述
partition 在指定范围内,将元素分为两组:满足指定一元谓词的元素放在第一个组中,其他元素放在第二个组中。不一定会保持集合中元素的相对位顺序
stable_partition 与partition一样,将指定范围分为两组,但保持元素的相对顺序不变

(8) 可用于排序容器的算法

算法 描述
binary_search 用于判断一个元素是否存在于一个排序集合中
lower_bound 根据元素的值或二元谓词判断元素可能插入到排序集合中的第一个位置,并返回一个指向该位置的迭代器
upper_bound 根据元素的值或二元谓词判断元素可能插入到排序集合中的最后一个位置,并返回一个指向该位置的迭代器

三、STL 常用算法示例

3.1 根据值或条件查找元素find()

find()可用于在vector等容器中查找与值匹配的元素,用法如下:

auto iElement = find(vec.begin(), vec.end(), NumToFind);
// 判断查找是否成功
if(iElement != vec.end())
	cout<<"Result:Value Found!"<<endl;

find_if()的用法与此类似,但需要通过第三个参数提供一个一元谓词(返回true或false的一元函数),用法如下:

// 查找偶数
auto iElement = find_if(vec.begin(), vec.end(), [](int element){ return (element % 2)==0;});
// 判断是否查找成功
if(iElement != vec.end())
	cout<<"Result:Value found!"<<endl;

下面我们就来实战演练一下,使用find()在vector中查找一个整数,并使用find_if()以及一个用lambda表达式表示的一元谓词查找第一个偶数:

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;

//display vector
	void Display(vector<int> vec)
	{ 
		for(int i=0; i<vec.size(); i++)
			cout<<vec[i]<<" ";
		cout<<endl;
	}
	
int main()
{ 	
	vector<int> vecIntegers;
	for(int value = 0; value<10; ++value)
		vecIntegers.push_back(value);
	cout<<"原始的vecIntegers是:"<<endl;
	Display(vecIntegers);
	
	// 查找vecIntegers中元素为5的值,返回迭代器的位置 
	auto iElement = find(vecIntegers.begin(),vecIntegers.end(),5);
	if(iElement != vecIntegers.end())
		cout<<"查找到该值:"<<*iElement<<endl;
	else
		cout<<"没有查找到该值" <<endl;
		
	// 查找第一个偶数元素
	auto iElement1 = find_if(vecIntegers.begin(), vecIntegers.end(), [](int element){ return (element % 2)==0;});
	if(iElement1 != vecIntegers.end())
		cout<<"第一个偶数元素是:"<<*iElement1<<endl;
	else
		cout<<"没有查找到偶数元素" <<endl;	
	return 0;
}

运行结果:

3.2 计算包含给定值或满足给定条件的元素数count()

count()和count_if()计算给定范围内的元素数。find()计算包含给定值的元素值:

size_t nNumZero = count(vec.begin(), vec.end(), 0);
cout<<"数组vec中值为0的个数:"<<nNumZero<<endl;

count_if()计算这样的元素数,即满足通过参数传递的一元谓词(可以是函数对象,也可以是lambda表达式):

template <typename elementType>

// 判断是否是偶数
bool IsEven(const elementType& number)
{ 
	return((number % 2) == 0);
}
// 计算偶数的个数
size_t nNumEvenElements = count_if(vec.begin(), vec.end(), IsEven <int>);
cout<<"数组vec中偶数的个数:"<<nNumEvenElements<<endl;

下面实战演练一下,使用count()和count_if()分别计算有多少个元素包含指定值满足指定条件

#include<iostream>
#include<algorithm>
#include<vector>

using namespace std;
template <typename elementType>

bool IsEven(const elementType& number)
{ 
	return((number % 2) == 0);
}

void Display(vector<int> vec)
{ 
	for(int i=0; i<vec.size(); i++)
		cout<<vec[i]<<" ";
}

int main()
{ 
	vector <int> vec;
	
	for(int i=0; i<10; ++i)
		vec.push_back(i);
		
	cout<<"数组vec:";	
	Display(vec);
	cout<<endl<<endl;
	// 使用count()统计数组中0的个数 
	size_t nNumZero = count(vec.begin(), vec.end(), 0);
	cout<<"数组vec中的0的个数:"<<nNumZero<<endl<<endl;
	// 使用count_if()统计满足偶数条件的值的个数 
	size_t nNumEvenElements = count_if(vec.begin(), vec.end(), IsEven <int>);
	cout<<"数组vec中偶数的个数:"<<nNumEvenElements<<endl;
	
	return 0;
} 

运行结果:

3.3 在集合中搜索元素或序列search()

前面学习的find()可以在容器中查找元素,但有时需要查找一个序列或模式;在这种情况下,应该使用serach()和search_n()。

  • search()用于在一个序列中查找另一个序列,比如在数组vec中查找list序列的位置:
auto iRange = search(vec.begin(), vec.end(), list.begin(), list.end());
  • search_n()用于在一个容器中查找n个相邻的指定值:
// 寻找999在数组vec中出现的位置
auto iRange = search_n(vec,begin(), vec.end(), 3, 9) 

这两个函数都返回一个迭代器,它指向找到的第一个位置。使用该迭代器之前,务必将其与end()进行比较,下面将演示search()和search_n()的用法:

#include<iostream>
#include<vector>
#include<list>
#include<algorithm>

using namespace std;

template <typename T>

void Display(const T& Input)
{ 
	for(auto i=Input.begin(); i!=Input.end(); ++i)
		cout<<*i<<" ";
	cout<<endl<<endl;
} 

int main()
{ 
	vector <int> vec;
	for(int i=0; i<10; i++)
		vec.push_back(i);
	
	vec.push_back(9);
	vec.push_back(9);
	cout<<"vec = ";
	Display(vec);
	
	list <int> listNums;
	for(int j=5; j<=8; ++j)
		listNums.push_back(j);
	cout<<"listNums = "; 
	Display(listNums);
	
	cout<<"serach()用法示例:" <<endl;
	auto iRange = search(vec.begin(), vec.end(), listNums.begin(), listNums.end());
	if(iRange != vec.end())
		cout<<"listNUms found in vec at position: "<<distance (vec.begin(), iRange)<<endl<<endl; 
	else 
		cout<<"vec中不存在这样的listNums!"<<endl;
	
	cout<<"search_n()用法示例:"<<endl;	
	auto iFound = search_n(vec.begin(), vec.end(), 3, 9);
	if(iFound != vec.end())
		cout<<"999 found in vec at position: "<<distance(vec.begin(), iFound)<<endl;
	else
		cout<<"999 isn't in vec!"<<endl; 
	 
	return 0;
}

运行结果:

3.4 将容器中的元素初始化为指定值fill()

STL算法提供了两个常用的函数来完成此任务:

  • fill()将指定范围内的元素设置为指定值
  • fill_n()将n个元素设置为指定的值。接受的参数包括:起始位置、元素的个数以及设置的值。

下面就实战演练一下,使用fill()和fill_n()设置容器中元素的初始值。

#include<iostream>
#include<vector>
#include<algorithm>

using namespace std;
void Display(vector <int> vec)
{ 
	for(int i=0; i<vec.size(); i++)
		cout<<vec[i]<<" ";
	cout<<endl;
	
} 

int main()
{ 
	// 初始化长度为3的数组 
	vector <int> vec(3);
	// 用9填满容器 
	fill(vec.begin(), vec.end(), 9);
	Display(vec);
	
	vec.resize(6);
	Display(vec);
	// 从第三个元素之后,填3个-9
	fill_n(vec.begin()+3, 3, -9); 
	Display(vec);
	
	return 0;
} 

运行结果:

四、STL 进阶算法示例

4.1 复制操作copy()

STL 主要提供了两个重要的复制函数:copy() 和 copy_if()

  • copy()沿向前的方向将源范围的内容赋给目标范围,返回的iPos是复制后,迭代器在vec中的位置:
auto iPos = copy(List.begin()  // 复制源的起始位置
			   , List.end()    // L复制源的最终位置
			   , vec.begin());  // 复制到vec的起始位置
  • copy_if()仅在指定的一元谓词返回true时才复制元素:
copy_if(List.begin() //复制源的起始位置
	  , List.end()  //复制源的最终位置
	  , iPos;  // 复制到指定容器的位置
	  ,  [](int i){ return ((i % 2)==1);});  //一元谓词,选取指定条件的元素执行复制操作

下面举一个简单的例子,来演示一下用法:

#include<iostream>
#include<list>
#include<vector>
#include<algorithm>
using namespace std;

template <typename T>
void Display(const T& Input)
{ 
	for(auto i=Input.begin(); i!=Input.end(); ++i)
		cout<< *i << " ";
	cout<<endl;
}

int main()
{ 
	list <int> List;
	for(int i=0; i<10; ++i)
		List.push_back(i);
	cout<<"List = ";
	Display(List);
	
	// 初始化数组 
	vector <int> vec(20);
	
	// 将List复制给vec,并返回复制后迭代器的位置 
	auto iPos = copy(List.begin(), List.end(), vec.begin());
	
	// 将List中的奇数按顺序复制到vec 
	copy_if(List.begin(), List.end(), iPos, [](int i){ return ((i % 2)==1);});
	cout<<"vec = "; 
	Display(vec);
	
	return 0;
}

运行结果:

4.2 删除操作erase()

STL 中也提供了两个重要的删除操作函数:remove() 和 remove_if()

  • remove()将容器中与指定值匹配的元素删除:
// 删除vec中值为0的全部元素
auto iRemove = remove(vec.begin(), vec.end(), 0);
  • remove_if()使用一个一元谓词,并将容器中满足该谓词的元素删除:
// 删除vec中的所有奇数
iRemove = remove_if(vec,begin(), vec.end(), [](int i){ return ((i % 2)==1);});

下面举一个简单的例子,来演示一下用法:

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

template <typename T>
void Display(const T& Input)
{ 
	for(auto i=Input.begin(); i!=Input.end(); ++i)
		cout<< *i << " ";
	cout<<endl;
}

int main()
{ 
	vector <int> vec;
	for(int i=0; i<10; ++i)
		vec.push_back(i);
	cout<<"vec = ";
	Display(vec);
	
	cout<<"删除所有值为0的元素:"<<endl;
	auto iRemove = remove(vec.begin(), vec.end(), 0);
	vec.erase(iRemove, vec.end());	
	Display(vec);
	
	cout<<"删除所有的奇数:"<<endl; 
	iRemove = remove_if(vec.begin(),vec.end(),[](int i){ return ((i % 2)==1);});
	vec.erase(iRemove, vec.end());	
	Display(vec);

	return 0;
}

运行结果:

4.3 替换操作replace()

STL 中也提供了两个重要的替换操作函数:replace() 和 replace_if()

  • replace()用于替换集合中等于指定值的元素:
// 将vec中的值为1的元素全部替代为2
replace(vec.begin(), vec.end(), 1, 2);
  • replace_if()用于替换集合中满足指定条件的元素:
// 将vec中的值为偶数的元素全部替代为0
replace_if(vec.begin(), vec.end(), [](int i){ return ((i % 2)==0);}, 0);

下面举一个简单的例子,来演示一下用法:

#include<iostream>
#include<algorithm>
#include<vector>

using namespace std;

void Display(vector<int> vec)
{ 
	for(int i=0; i<vec.size(); ++i)
	{ 
		cout<<vec[i]<<" "; 
	}
	cout<<endl;
}

int main()
{ 
	vector <int> vec(10, 1);
	cout<<"vec ="; 
	Display(vec);
	
	cout<<"将vec中的值为1的元素全部替代为2:" <<endl;
	replace(vec.begin(), vec.end(), 1, 2);
	Display(vec);
	
	cout<<"将vec中的值为偶数的元素全部替代为0:" <<endl;
	replace_if(vec.begin(), vec.end(), [](int i){ return ((i % 2)==0);}, 0);
	Display(vec);
	
	return 0;
} 

运行结果:

4.4 排序操作sort()

为了方便对一组信息进行排序,C++ STL算法专门提供了sort()函数。下面举例几种sort()的常用情况:

  • 默认情况下,sort()进行升序排序,方法如下:
// 对vec中的元素进行升序排序
sort(vec.begin(), vec.end());
  • 如果要改变默认的排序标注,需要专门指定谓词;比如下面进行降序排序:
// 对vec中的元素进行降序排序
sort(vec.begin(), vec.end(), [](int a, int b){ return(a>b);});

同样,也可以用如下方式实现:

// 自定义降序排序谓词
bool compare(int a, int b){   
	return a>b;   
}
// 对vec中的元素进行降序排序
sort(vec.begin(), vec.end(), compare);
  • 除了对整型数组进行排序,也可以对字符串进行排序。
bool compare(string s1, string s2){ 
	return s1 > s2;
}
sort(arr_str.begin(), arr_str.end(), compare)

下面举一个简单的例子,来演示一下用法:

#include<iostream>
#include<algorithm>
#include<vector>

using namespace std;

template <typename T>
void Display(const T& Input)
{ 
	for(auto i=Input.begin(); i!=Input.end(); ++i)
		cout<< *i << " ";
	cout<<endl;
}

int main()
{ 
	vector<int> vecNums;
	for(int i=0; i<10; ++i)
		vecNums.push_back(i);
	cout<<"vecNums = ";
	Display(vecNums);
	
	cout<<"降序排序:"<<endl;
	sort(vecNums.begin(), vecNums.end(), [](int a, int b){ return(a>b);});
	Display(vecNums);
	
	
	return 0;
}

运行结果:

下面演示一下,对字符串数组使用sort()的例子:

#include<iostream>
#include<algorithm>
#include<vector>
#include<string>

using namespace std;

template <typename T>
void Display(const T& Input)
{ 
	for(auto i=Input.begin(); i!=Input.end(); ++i)
		cout<< *i << endl;
	cout<<endl;
}

int main()
{ 
	vector<string> vecNames;
	vecNames.push_back("Jack");
	vecNames.push_back("Bob");
	vecNames.push_back("Dove");
	vecNames.push_back("Anna");
	cout<<"原始的vecNames:"<<endl;
	Display(vecNames);
	 
	cout<<"升序排序:"<<endl; 
	sort(vecNames.begin(), vecNames.end()); 
	Display(vecNames);
	
	cout<<"降序排序:"<<endl;  
	sort(vecNames.begin(), vecNames.end(), [](string a, string b){ return(a>b);});
	Display(vecNames);
	
	return 0;
}

运行结果:

4.5 删除重复元素操作unique()

在容器中,如果有重复的元素;可以使用STL算法中的unique()删除相邻的重复值:

auto iDel = unique(vec.begin(), vec.end())
vec.erase(iDel, vec.end());

下面举一个简单的例子,来演示一下用法:

#include<iostream>
#include<algorithm>
#include<vector>
#include<string>

using namespace std;

template <typename T>
void Display(const T& Input)
{ 
	for(auto i=Input.begin(); i!=Input.end(); ++i)
		cout<< *i << endl;
	cout<<endl;
}

int main()
{ 
	vector<string> vecNames;
	vecNames.push_back("Jack");
	vecNames.push_back("Bob");
	vecNames.push_back("Dove");
	vecNames.push_back("Jack");
	cout<<"原始的vecNames:"<<endl;
	Display(vecNames);
	
	cout<<"删除数组中重复的元素:"<<endl;
	auto iDel = unique(vecNames.begin(), vecNames.end());
	vecNames.erase(iDel, vecNames.end());
	Display(vecNames);
	
	cout<<"升序排序:"<<endl; 
	sort(vecNames.begin(), vecNames.end()); 
	Display(vecNames);
	
	cout<<"删除数组中重复的元素:"<<endl;
	auto iDel0 = unique(vecNames.begin(), vecNames.end());
	vecNames.erase(iDel0, vecNames.end());
	Display(vecNames);
	
	return 0;
}

运行结果:

分析:

第一次使用unique()并没有删除重复的元素"Jack";但是,经过升序排序后,第二次再使用uniqueI()成功删除了重复的元素。这是因为,经过排序后,两个Jack成了相邻的元素。而unique()恰恰删除的是相邻的元素,对于不相邻的元素并无作用。

4.6 插入元素操作insert()

使用insert()在容器指定位置执行插入操作时,需要先知道待插入的位置。大多数情况下,我们可以使用find()找到待插入元素的位置。这里介绍另外一种方法,使用STL提供的 lower_bound()upper_bound() 确定待插入元素位置。

  • 使用lower_bound()在指定元素前插入新的元素
auto iMinPos = lower_bound(vecNames.begin(), vecNames.end(), "Dove");
vecNames.insert(iMinPos, "Lucy");
  • 使用upper_bound()在指定元素后插入新的元素
auto iMaxPos = upper_bound(vecNames.begin(), vecNames.end(), "Dove");
vecNames.insert(iMaxPos, "Lucy");
#include<iostream>
#include<algorithm>
#include<vector>
#include<string>
#include<list>

using namespace std;

template <typename T>
void Display(const T& Input)
{ 
	for(auto i=Input.begin(); i!=Input.end(); ++i)
		cout<< *i << endl;
	cout<<endl;
}

int main()
{ 
	vector<string> vecNames;
	vecNames.push_back("Jack");
	vecNames.push_back("Bob");
	vecNames.push_back("Dove");
	vecNames.push_back("Jack");
	cout<<"原始的vecNames:"<<endl;
	Display(vecNames);
	
	cout<<"升序排序:"<<endl; 
	sort(vecNames.begin(), vecNames.end()); 
	Display(vecNames);

	cout<<"在Dove前插入新的元素:Lucy"<<endl;
	auto iMinPos = lower_bound(vecNames.begin(), vecNames.end(), "Dove");
	vecNames.insert(iMinPos, "Lucy");
	
	//在Dove后插入新的元素 
	//auto iMaxPos = upper_bound(vecNames.begin(), vecNames.end(), "Dove");
	//vecNames.insert(iMaxPos, "Lucy");
	
	Display(vecNames);
	return 0;
}

运行结果:

今天的分享就到这里啦,希望对你的学习有所帮助!

养成习惯,先赞后看!你的支持是我创作的最大动力!

更多文章可戳戳 :

【C++养成计划】不聊学习只谈干货(Day1)

 
打赏
 本文转载自:网络 
所有权利归属于原作者,如文章来源标示错误或侵犯了您的权利请联系微信13520258486
更多>最近资讯中心
更多>最新资讯中心
0相关评论

推荐图文
推荐资讯中心
点击排行
最新信息
新手指南
采购商服务
供应商服务
交易安全
关注我们
手机网站:
新浪微博:
微信关注:

13520258486

周一至周五 9:00-18:00
(其他时间联系在线客服)

24小时在线客服