C++算法与泛型算法(algorithm、numeric)

本文包括的算法有:

  • 只读算法:find()、count()、accumulate()、equal()
  • 写算法:fill()、fill_n()、back_inserter()、copy()、copy_backward()、replace()、replace_copy()、next_permutation()、prev_permutation()
  • 重排元素算法:sort()、stable_sort()、unique()

一、算法简介

大多数算法在头文件algorithm中。标准库还在头文件numeric中定义了一组数值泛型算法
算法是如何工作的:

  • 迭代器令算法不依赖于容器类型:算法不依赖于容器所保存的元素类型。只要有一个迭代器可以来访问元素,就可以进行运算
  • 但算法依赖于元素类型的操作:虽然迭代器令算法不依赖于容器类型,但大多数算法都是用了一个(多个)元素类型上的操作。例如find用元素类型的==运算符完成每个元素与给定值的比较。其他算法可能要求元素类型支持“<”运算符。不过,我们将看到,大多数算法提供了一种方法,允许我们使用自定义的操作来替代默认的运算符

二、泛型算法

  • 标准库提供了超过100个算法,这些算法都对一个范围内的元素来进行操作
  • 算法基本上分为3类:是否读取元素、改变元素、重排元素顺序

三、只读算法

  • 只可以操作容器元素,不可以改变容器内的值
  • 因为是只读,所以建议使用只读迭代器(cbegin()、cend())

find()

  • 功能:遍历一个范围中是否包含某元素
  • 参数:前2个参数是一个迭代器范围或者指针范围。第3个参数是要查找的元素
  • 返回值:成功返回要查找的元素所在的迭代器。失败返回参数2
//判断value在vec中是否存在,因为find如果失败返回的是参数2.所以可以用来判断是否查找成功

vector<int> vec{ 1,2,3};
int value = 2;
auto result=find(vec.cbegin(),vec.cend(), value);
cout << "The value " << value << (result == vec.cend()
 ? "is not present" : "is present") << endl;
vector<string> vec{ "A","B","C" };
auto result=find(vec.cbegin(),vec.cend(), "B");
cout << "The B "<< (result == vec.cend()
 ? "is not present" : "is present") << endl;

对数组的操作:可以用内置函数begin()、end()作为数组的迭代器,也可以用指针作为迭代器

int arr[] = { 1,2,3,4,5 };
int val = 4;
int* result = find(begin(arr), end(arr), val);
if (result != end(arr)) {
 cout << "find succcess,value is:"<< *result<< endl;
}
int arr[] = { 1,2,3,4,5 };
int value = 3;
auto result = find(arr + 1, arr + 3, value);
cout << "The value "<<value<<((result == arr + 3)
	?" is not present":"is present")<< endl;

count()

  • 功能:返回元素在指定迭代器范围内出现的次数
  • 返回值:成功返回出现的次数,没有返回0
list<int> li{ 1,2,3,66,66,66,100 };
cout <<"The 66 count is:"
	<<count(li.cbegin(),li.cend(),66)<< endl;

accumulate()

  • 头文件:numeric
  • 功能:将指定范围内的元素进行和运算,参数3为和运算的初始值
  • 返回值:返回和运算的结果
  • 注意:此函数操作的元素必须能够与+运算符进行操作
//计算li元素的和,和的初始值为0
list<int> li{ 1,2,3 };
cout <<"The sum is:" <<accumulate(li.cbegin(),li.cend(),0)<< endl; //6

使用string时,必须显示地调用,不能够直接使用字符串,因为这样会被accumulate函数认为是一个const char*对象而出错

//正确
ist<string> li{"A","B","C"};
cout <<accumulate(li.cbegin(),li.cend(),string("String:"))<< endl; //String:ABC
//错误
list<string> li{"A","B","C"};
cout <<accumulate(li.cbegin(),li.cend(),"String:")<< endl;
//正确
list<string> li{"A","B","C"};
string s = "String:";
cout <<accumulate(li.cbegin(),li.cend(), s)<< endl;

附加:如果想要进行别的运行,例如乘、除等,可以使用参数4.例如下面是对一个数组内的元素进行乘操作(备注:初始化不要填0,否则结果就为0)

int *p = new int[4] {1, 2, 3, 4};
cout << accumulate(p, p + 4, 1,multiplies<int>()) << endl; //24

equal()

  • 功能:用来比较两个指定范围内的元素是否都是相同的值(常来比较两个元素是否相同)
  • 参数:参数1和参数2指定一个容器的范围。参数3指定另一个容器的开始范围
  • 比较规则:将参数1和2指定的范围内的所有元素,查看这些元素是否与参数3所指定的另一个容器的开始处是否都存在(不一定要个数都相同,只要容器1的元素在容器2中都要一一对应)
  • 返回值:都相同返回1。不相同返回0
  • 因为equal调用迭代器完成操作,所以equal可以用来比较两个不同类型的容器。例如vector<string>和list<const char*>可以进行比较
vector<int> vec1{ 1,2};
vector<int> vec2{ 1,2,3};
vector<int> vec3{ 1,2,3,4};
vector<int> vec4{ 1,2,3,4 };

cout << equal(vec1.cbegin(),vec1.cend(), vec4.cbegin())<< endl; //1
cout << equal(vec2.cbegin(), vec2.cend(), vec4.cbegin()) << endl; //1
cout << equal(vec3.cbegin(), vec3.cend(), vec4.cbegin()) << endl; //1
vector<int> vec1{ 2,3};
vector<int> vec2{ 1,2,3,4 };

cout << equal(vec1.cbegin(),vec1.cend(), vec2.cbegin())<< endl; //0
vector<string> vec1{ "A","B"};
vector<string> vec2{ "B" };
vector<const char*> vec3{ "A","B","C" };

cout << equal(vec1.cbegin(), vec1.cend(), vec3.cbegin()) << endl; //1
cout << equal(vec2.cbegin(), vec2.cend(), vec3.cbegin())<< endl; //0

四、写算法

可以读写容器内的元素,但不可以改变容器的大小。因此操作时要注意容器的大小(例如不能对空容器操作)

因为会改变值,所以不能使用只读迭代器(cbegin()、cend())

fill()

  • 用来改变指定位置处的元素
  • 参数:参数1、2指定容器的范围,参数3位要设置的值
vector<int> vec{ 1,2,3,4,5 };
fill(vec.begin(), vec.end(), 0);//将vec全部置为0
for (auto v = vec.cbegin(); v != vec.cend(); v++)
	cout << *v << endl;
vector<int> vec{ 1,2,3,4,5,6 };
fill(vec.begin(), vec.begin()+vec.size()/2, 66); //将vec的前半部分元素变为66

for (auto v = vec.cbegin(); v != vec.cend(); v++)
	cout << *v << endl;

fill_n()

  • 用来将指定数量的元素变为某个值
  • 参数:参数1为迭代器起始位置。参数2为要改变的元素个数。参数3为要设定的值
  • 注意:要注意参数2的设定,不能超出容器的大小,也不能对空容器操作
vector<int> vec{ 1,2,3,4,5,6 };

fill_n(vec.begin(), 3, 66); //将vec的前3个元素变为66
for (auto v = vec.cbegin(); v != vec.cend(); v++)
	cout << *v << endl;

fill_n(vec.begin(), vec.size(), 66); //将vec全部变为66
for (auto v = vec.cbegin(); v != vec.cend(); v++)
	cout << *v << endl;
//下面代码不会出错,但是也不会有效果,因为vec是空向量

vector<int> vec;
fill_n(vec.begin(), vec.size(), 66);
for (auto v = vec.cbegin(); v != vec.cend(); v++) //不打印任何信息
	cout << *v << endl;

back_inserter()

  • 又名插入迭代器
  • 参数:为一个容器的引用
  • 返回值:返回与该容器绑定的插入迭代器
  • 功能:常用来返回一个容器的迭代器,然后对此迭代器进行操作
  • 当我们通过返回的插入迭代器赋值时,会自动调用push_back将一个具有给定值的元素添加到容器中
  • 头文件iterator
vector<int> vec; //空容器

auto it = back_inserter(vec); //返回vec的第一个迭代器
*it = 42; //此时vec有了一个元素,值为42

现在我们可以使用fill_n来给一个空容器赋值:在每次迭代中,back_inserter返回迭代器,因此每次赋值都会在vec上调用push_back,因此fill_n就能够操作了。下面是在vec的末尾添加10个新的元素

vector<int> vec;
fill_n(back_inserter(vec), 10, 0);//通过back_inserter创建一个插入迭代器,然后可以向vec添加元素了
for (auto v = vec.cbegin(); v != vec.cend(); v++) //打印10个0
	cout << *v << endl;

copy()

  • 将参数1、2指定范围的元素拷贝给参数3指定的容器
  • 参数:参数1、2为一个容器的范围。参数3要接受拷贝的容器起始位置
  • 注意:参数3要有足够的空间保存拷贝的数据
  • 返回值:返回拷贝目的位置的迭代器值
int arr1[] = { 1,2,3 };
int arr2[sizeof(arr1)/sizeof(*arr1)];

auto ret = copy(begin(arr1), end(arr1), arr2); //将数组1拷贝给数组2。ret为arr2数组最后元素的下一个位置

for (auto i = 0; i < sizeof(arr2) / sizeof(*arr2); i++) {
 cout << arr2[i]<< endl;
}

copy_backward

  • 该函数与copy的不同之处在于:

    • copy是从第一个元素开始拷贝,而copy_backward是从最后一个元素开始拷贝
    • copy的第3个参数是接受拷贝的容器起始位置,而copy_backward是目的序列的结束迭代器
  • 会复制前两个迭代器参数指定的序列。第三个参数是目的序列的结束迭代器

replace()

  • 将指定范围内指定的元素替换为另一个值
  • 参数:1、2指定替换的范围。3:目标值,4:替换后的值
vector<int> vec{ 1,0,0,0,5 };
replace(vec.begin(), vec.end(), 0,66); //将vec中为0的全部替换为66

for (auto v = vec.cbegin(); v != vec.cend(); v++) {
	cout << *v << endl;
}

replace_copy()

  • 此函数会保留原容器不变,然后将替换后的结果保存在另一个容器中
  • 参数:参数12位要替换的范围。参数3位另一个容器的起始位置。参数4目标值。参数5替换后的值
vector<int> vec{ 1,0,0,0,5 };
vector<int> vec2;

replace_copy(vec.begin(), vec.end(),back_inserter(vec2), 0,66); //vec的元素保持不变,vec2为替换后的值

for (auto v = vec2.cbegin(); v != vec2.cend(); v++) {
 cout << *v << endl;
}

next_permutation()

  • 功能:对一个迭代区间的数值进行全排列
  • 返回值:会根据前面next_permutation函数的调用,当操作的区间不存在下一个排列时,函数返回false;否则如果可以继续进行全排列,那么就返回true
//函数原型
#include <algorithm>
bool next_permutation(iterator start,iterator end)

演示案例:下面的程序每调用一次next_permutation就会对我们制定的迭代区间进行一次排列,直到得出全部排列之后,返回false

int *p = new int[3] {1, 2, 3};
do {
 for (int i = 0; i < 3; ++i){
  cout << p[i];
 }
 cout << endl;
} while (next_permutation(p, p + 3));

prev_permutation()

  • 与next_permutation的功能显示,区别就是prev_permutation是求当前排列的前一个排列,而next_permutation是求当前排列的下一个排列

五、重排元素算法

sort()、unqie()、stable_sort()

因为会改变值,所以不能使用只读迭代器(cbegin()、cend())

sort()

  • 将容器内的元素按照运算符“<”的规则排序,从小到大
  • 参数:一个容器的迭代器范围
vector<int> vec{ 1,4,2,8,4,1 };
sort(vec.begin(), vec.end()); //将vec的值从小到大排序

for (auto v = vec.cbegin(); v != vec.cend(); v++) {
 cout << *v << endl;
}

unique()

  • 相邻之间如果有重复相同的元素,则删除重复的元素只保留一个
  • 参数:一个容器的迭代器范围
  • 返回值:返回删除重复元素之后的最后一个元素的后一个位置
  • 注意(重点):虽然删除了元素,但是容器的大小依然没有变,迭代器也没有变。所有变为迭代器时一定要注意

vector<int> vec{ 1,1,2,3,4,4,4,5,1 };
auto end_unique=unique(vec.begin(), vec.end());

//for循环时使用unique的返回值,如果使用vec.cend(),那么会打印后面没有元素位置的乱值
for (auto v = vec.cbegin(); v != end_unique; v++) {
	cout << *v << endl;
}

stable_sort()

  • 也是排序
  • 如果非字符串,就先按照数值的个数排序,个数相同时再按照大小排序
  • 如果是字符串:按照长度排序,长度相同的按照字典排序
vector<int> vec{ 1,10,2,100,4,1 };
stable_sort(vec.begin(), vec.end());

//1,1,2,4,10,100
for (auto v = vec.cbegin(); v != vec.cend(); v++) {
 cout << *v << endl;
}

六、向算法传递函数和lambda表达式

注意事项:

向算法传递函数或者lambda表达式时要注意。该算法支持调用一元谓词(参数)还是多元谓词(参数)

例如:find_if的第3个参数传递的函数/lambda只接受一个参数,如果是两个参数的函数或lambda,那么就不能调用(后面会介绍bind函数解决这个问题)

sort()

bool isMin(const int &s1, const int & s2) {
 return s1 < s2;
}

bool isMax(const int &s1, const int & s2) {
 return s1 > s2;
}

vector<int> vec{1,2,3,4,5};
sort(vec.begin(), vec.end(), isMin); //从小到大排序
sort(vec.begin(), vec.end(), isMax); //从大到小排序

//使用lambda表达式
sort(vec.begin(), vec.end(),
 [](const int &a, const int &b) {return a < b; });//从小到大排序
sort(vec.begin(), vec.end(),
 [](const int &a, const int &b) {return a > b; });//从大到小排序

stable_sort()

//按照长度排序,长度相同的按照字典排序
bool isShorter(const string &s1, const string & s2) {
	return s1.size() < s2.size();
}

vector<string> vec{"fox","jumps","over","quick","res","slow","the","turtle"};
stable_sort(vec.begin(), vec.end(), isShorter);

for (auto v = vec.cbegin(); v != vec.cend(); v++) {
	cout << *v << endl;
}

find_if()

  • 用来在指定范围内查找比指定值大/下的元素
  • 如果是大于(那么就是最大的那个)。如果是小于(那么就是比他小一个的那个)
  • 参数3:为一个函数指针或者lambda表达式
  • 返回值:存在就返回这个值,不存在返回尾迭代器
vector<int> vec{ 1,2,3,4,5 };
int sz = 4;

//在vec中寻找比sz大的数
auto wc=find_if(vec.begin(), vec.end(),
 [sz](const int &a) {return a > sz; }); //查找比sz大的
auto wc2=find_if(vec.begin(), vec.end(),
		[sz](const int &a) {return a < sz; }); //查找比sz小的

cout << *wc << endl; //5
cout << *wc2 << endl; //3

for_each()

  • 用来遍历一个集合
  • 参数:参数12是一个容器的迭代器范围。参数3lambda表达式
vector<int> vec{ 1,2,3,4,5 };

for_each(vec.begin(), vec.end(),
 [](const int&s) {cout << s << " "; });
cout << endl;
vector<string> vec{ "ABC","AB","A","sd" };

for_each(vec.begin(), vec.end(),
 [](const string&s) {cout << s << " "; });
cout << endl;

transform()

  • 参数:参数12为输入序列。参数3为目的位置
  • 该算法对输入序列中每个元素调用课调用对象,并将结果写到目的位置
  • 下面使用transform算法和一个lambda表达式来将vector中的每个负数替换为绝对值。因为参数3位vec.begin(),所以就是将vec的全部元素取绝对值然后再写入vec的开头
vector<int> vec{ -1,-2,-3,4 };
//将vec的数全部取绝对值
transform(vec.begin(), vec.end(), vec.begin(),
		[](int i) {return i < 0 ? -i : i; });

for (auto v = vec.begin() ; v != vec.end(); ++v)
	cout <<*v << endl;

到此这篇关于C++算法与泛型算法(algorithm、numeric)的文章就介绍到这了,更多相关C++ algorithm numeric内容请搜素我们以前的文章或下面相关文章,希望大家以后多多支持我们!

(0)

相关推荐

  • C++泛型算法的一些总结

    泛型算法的一些总结1.每个泛型算法的实现都独立于单独的容器,并且不依赖于容器存储的元素类型. 2.泛型算法从不直接添加或删除元素. 3.与容器的类型无关,只在一点上隐式地依赖元素类型:必须能够对元素做比较运算. A.需要某种遍历集合的方式:能够从一个元素向前移到下一个元素. B.必须能够知道是否到达了集合的末尾. C.必须能够对容器中的每一个元素与被查找的元素进行比较. D.需要一个类型来指示元素在容器中的位置,或者表示找不到该元素. 4.迭代器将算法和容器绑定起来.算法基于迭代器及其操作实现,

  • C++算法与泛型算法(algorithm、numeric)

    本文包括的算法有: 只读算法:find().count().accumulate().equal() 写算法:fill().fill_n().back_inserter().copy().copy_backward().replace().replace_copy().next_permutation().prev_permutation() 重排元素算法:sort().stable_sort().unique() 一.算法简介 大多数算法在头文件algorithm中.标准库还在头文件numer

  • C++ primer超详细讲解泛型算法

    目录 初识泛型算法 只读算法 写容器算法 定制操作 lambda表达式 lambda捕获和返回 再探迭代器 插入迭代器 iostream迭代器 反向迭代器 初识泛型算法 只读算法 只读取输入范围内的函数,不改变元素,find,accumula也是如此 (1)accumulate算法为求和算法,前两个参数指出求和元素范围,第三个是和的初值,例: int sum=accumulate(v.begin(),v.end(),0) (2)操作两个序列的算法 equal算法,确定两个序列是否保存相同的值,将

  • java实现最短路径算法之Dijkstra算法

    前言 Dijkstra算法是最短路径算法中为人熟知的一种,是单起点全路径算法.该算法被称为是"贪心算法"的成功典范.本文接下来将尝试以最通俗的语言来介绍这个伟大的算法,并赋予java实现代码. 一.知识准备: 1.表示图的数据结构 用于存储图的数据结构有多种,本算法中笔者使用的是邻接矩阵. 图的邻接矩阵存储方式是用两个数组来表示图.一个一维数组存储图中顶点信息,一个二维数组(邻接矩阵)存储图中的边或弧的信息. 设图G有n个顶点,则邻接矩阵是一个n*n的方阵,定义为: 从上面可以看出,无

  • Java贪心算法之Prime算法原理与实现方法详解

    本文实例讲述了Java贪心算法之Prime算法原理与实现方法.分享给大家供大家参考,具体如下: Prime算法:是一种穷举查找算法来从一个连通图中构造一棵最小生成树.利用始终找到与当前树中节点权重最小的边,找到节点,加到最小生成树的节点集合中,直至所有节点都包括其中,这样就构成了一棵最小生成树.prime在算法中属于贪心算法的一种,贪心算法还有:Kruskal.Dijkstra以及哈夫曼树及编码算法. 下面具体讲一下prime算法: 1.首先需要构造一颗最小生成树,以及两个节点之间的权重数组,在

  • 字符串的模式匹配详解--BF算法与KMP算法

    一.BF算法     BF算法是普通的模式匹配算法,BF算法的思想就是将目标串S的第一个字符与模式串P的第一个字符进行匹配,若相等,则继续比较S的第二个字符和P的第二个字符:若不相等,则比较S的第二个字符和P的第一个字符,依次比较下去,直到得出最后的匹配结果. 举例说明: S: ababcababa P: ababa BF算法匹配的步骤如下 i=0 i=1 i=2 i=3 i=4 第一趟:ababcababa 第二趟:ababcababa 第三趟:ababcababa 第四趟:ababcabab

  • 算法之排序算法的算法思想和使用场景总结

    1. 概述 排序算法是计算机技术中最基本的算法,许多复杂算法都会用到排序.尽管各种排序算法都已被封装成库函数供程序员使用,但了解排序算法的思想和原理,对于编写高质量的软件,显得非常重要. 本文介绍了常见的排序算法,从算法思想,复杂度和使用场景等方面做了总结. 2. 几个概念 (1)排序稳定:如果两个数相同,对他们进行的排序结果为他们的相对顺序不变.例如A={1,2,1,2,1}这里排序之后是A = {1,1,1,2,2} 稳定就是排序后第一个1就是排序前的第一个1,第二个1就是排序前第二个1,第

  • Java实现的各种排序算法(插入排序、选择排序算法、冒泡排序算法)

    一.插入排序算法实现java版本 public static int[] insert_sort(int[] a) { for (int i = 0; i < a.length; i++) { for(int j=i+1;j>0&&j<a.length;j--) { if(a[j]<a[j-1]) { int tmp = a[j]; //这样定义初始化逻辑上是可以的,j变量,每次tmp的值变化的 a[j] = a[j-1]; a[j-1] = tmp; } } }

  • java数据结构与算法之插入算法实现数值排序示例

    本文实例讲述了java数据结构与算法之插入算法实现数值排序.分享给大家供大家参考,具体如下: 写在这里做个纪念,关键是要理解插入点,在插入点,初始的in和out都在这个插入点,然后通过in自减对数组进行重新排序 public static void insertSort(){ for(int out=1; out<a.length; out++){ int temp = a[out]; int in = out; while(in>0&& a[in-1]>temp){ a

  • java 合并排序算法、冒泡排序算法、选择排序算法、插入排序算法、快速排序算法的描述

    算法是在有限步骤内求解某一问题所使用的一组定义明确的规则.通俗点说,就是计算机解题的过程.在这个过程中,无论是形成解题思路还是编写程序,都是在实施某种算法.前者是推理实现的算法,后者是操作实现的算法. 一个算法应该具有以下五个重要的特征: 1.有穷性: 一个算法必须保证执行有限步之后结束: 2.确切性: 算法的每一步骤必须有确切的定义: 3.输入:一个算法有0个或多个输入,以刻画运算对象的初始情况: 4.输出:一个算法有一个或多个输出,以反映对输入数据加工后的结果.没有输出的算法是毫无意义的:

随机推荐