标准库容器定义的操作非常少,并没有给容器添加大量的功能函数,而是选择提供一组算法,这些算法大都不依赖特定的容器类型,是“泛型”的,可作用在不同类型的容器和不同类型的元素上!
所谓泛型算法:一是因为它们实现共同的操作,所以称之为“算法”;而“泛型”指的是它们可以操作在多种容器类型上——不但可作用于vector或list这些标准库类型,还可用在内置数组类型、甚至其他类型的序列上,只要自定义的容器类型只要与标准库兼容,同样可以使用这些泛型算法。
大多数算法是通过遍历由两个迭代器标记的一段元素来实现其功能。典型情况下,算法在遍历一段元素范围时,操纵其中的每一个元素。算法通过迭代器访问元素,这些迭代器标记了要遍历的元素范围。
find函数
可得治泛型算法基于迭代器实现,和容器类型无关
int searchVal = 110; vector<int>::const_iterator iter = find(ivec.begin(),ivec.end(),searchVal); if (iter != ivec.end()) { cout << "The value " << *iter << " is present!" << endl; } else { cout << "The value " << searchVal << " is not present!" << endl; }
list<int> iList; for (list<int>::size_type index = 0; index != 100; ++index) { iList.push_back(index); } int searchVal = 13; list<int>::const_iterator iter = find(iList.begin(),iList.end(),searchVal); if (iter != iList.end()) { cout << "The value " << *iter << " is present!" << endl; } else { cout << "The value " << searchVal << " is not present!" << endl; }
int ia[] = {27,210,12,476,109,83}; int searchVal = 109; int *result = find(ia,ia+sizeof(ia)/sizeof(*ia),searchVal); if (result != ia+sizeof(ia)/sizeof(*ia)) { cout << "The value " << *result << " is present!" << endl; } else { cout << "The value " << searchVal << " is not present!" << endl; }使用两个迭代器和一个值调用 find 函数 , 检查两个迭代器实参标记范围内的每一个元素。只要找到与给定值相等的元素 ,find 就会返回指向该元素的迭代器。如果没有匹配的元素 ,find 就返回它的第二个迭代器实参 , 表示查找失败。
该算法的明确要求如下:
1)需要某种遍历集合的方式:能够从一个元素向前移到下一个元素。
2)必须能够知道是否到达了集合的末尾。
3)必须能够对容器中的每一个元素与被查找的元素进行比较。
4)需要一个类型指出元素在容器中的位置,或者表示找不到该元素。
统计指定值出现的次数
//P338 习题11.1 int main() { ifstream inFile("input"); vector<int> iVec; int val; while (inFile >> val) { iVec.push_back(val); } int searchVal; while (cin >> searchVal) { cout << searchVal << " have present " << count(iVec.begin(),iVec.end(),searchVal) << " times" << endl; } }泛型算法本身从不执行容器操作,只是单独依赖迭代器和迭代器操作实现。算法基于迭代器及其操作实现,而并非基于容器操作。
泛化的算术算法,其命名习惯与泛型算法相同,使用这些算法必须包含numeric头文件
accumulate的使用:
一个简单的只读算法accumulate,该算法在numeric头文件中定义。
将sum设置为 vec的元素之和再加上0。accumulate带有三个形参。头两个形参指定要累加的元素范围。第三个形参则是累加的初值。
首先,调用该函数时必须传递一个起始值,否则,accumulate将不知道使用什么起始值。其次,容器内的元素类型必须与第三个实参的类型匹配,或者可转换为第三个实参的类型。在accumulate内部,第三个实参用作累加的起点;容器内的元素按顺序连续累加到总和之中。因此,必须能够将元素类型加到总和类型上。
类似的,也可以使用accumulate把string类型的vector容器中的元素连接起来:
【注意】
程序显式地创建了一个string对象,用该函数调用的第三个实参。传递一个字符串字面值,将会导致编译时错误。因为此时,累加和的类型将是constchar*,而 string的加法操作符所使用的操作数则分别是string和 constchar* 类型,加法的结果将产生一个string对象,而不是 constchar* 指针。
find_first_of的使用:
这个算法带有两对迭代器参数来标记两段元素范围,在第一段元素范围内查找与第二段范围中任意元素匹配的元素,然后返回一个迭代器,指向第一个匹配的元素,如果找不到匹配元素,则返回第一个范围的end迭代器。
find_first_of(iter,iVec1.end(),iVec2.begin(),iVec2.end())
由一个迭代器指定操作位置,而其余的参数一般就是初始化值,或者隐式的表达迭代器对应数据的类型,而返回值一般为一个迭代器,分别表示
要操作的正确结果和错误结果
写容器元素的算法
一些算法写入元素值。在使用这些算法写元素时一定要当心,必须确保算法所写的序列至少足以存储要写入的元素。
1、写入输入序列的元素
fill带有一对迭代器形参,用于指定要写入的范围,而所写的值是它的第三个形参的副本。如果输入范围有效,则可安全写入。这个算法只会对输入范围内已存在的元素进行写入操作。
2、不检查写入操作的算法(十分危险)
fill_n函数带有的参数包括:一个迭代器、一个计数器以及一个值。fill_n函数假定对指定数量的元素做写操作是安全的。
对于指定数目的元素做写入运算,或者写到目标迭代器的算法,都不检查目标的大小是否足以存储要写入的元素。
3、引入back_inserter
确保算法有足够的元素存储输出数据的一种方法是使用插入迭代器。在使用插入迭代器赋值时,会在容器中添加一个新元素,其值等于赋值运算的右操作数的值。
使用back_inserter的程序必须包含iterator头文件。
back_inserter函数是迭代器适配器。迭代器适配器使用一个对象作为实参,并生成一个适应其实参行为的新对象。在本例中,传递给back_inserter的实参是一个容器的引用。back_inserter生成一个绑定在该容器上的插入迭代器。在试图通过这个迭代器给元素赋值时,赋
值运算将调用push_back在容器中添加一个具有指定值的元素。使用 back_inserter可以生成一个指向fill_n写入目标的迭代器:
效果相当于在vec上调用push_back,在vec末尾添加10个元素,每个元素的值都是0。
4、写入到目标迭代器的算法
第三类算法向目标迭代器写入未知个数的元素。如:copy函数。copy带有三个迭代器参数:头两个指定输入范围,第三个则指向目标序列的一个元素。传递给copy的目标序列必须至少要与输入范围一样大。假设ilst是一个存放int型数据的 list对象,可如下将它copy给一 个vector对象:
copy从输入范围中读取元素,然后将它们复制给目标ivec。
当然,这个例子的效率比较差,最好应该对新构造容器的初始化式:
5、算法的_copy版本
有些算法提供所谓的“复制(copying)”版本。这些算法对输入序列的元素做出处理,但不修改原来的元素,而是创建一个新序列存储元素的处理结果。
replace算法就是一个很好的例子。该算法对输入序列做读写操作,将序列中特定的值替换为新的值。该算法带有四个形参:一对指定输入范围的迭代器和两个值。每一个等于第一值的元素替换成第二个值。
如果不想改变原来的序列,则调用replace_copy函数。这个算法接受第三个迭代器实参,指定要保存调整后序列的位置:
对容器元素重新排序的算法
我们要分析一组儿童故事中所使用的单词。例如:它们使用了多少个由六个或以上字母组成的单词。每个单词只统计一次。要求以长度的大小输出这些单词,对于同样长的单词,则以字典顺序输出。
1)、去除重复
假设我们的输入存储在一个名为words的 vector对象中,第一个子问题是将words中重复出现的单词去除掉:
unique算法带有两个指定元素范围的迭代器参数。该算法删除相邻的重复元素,然后重新排列输入范围内的元素,并且返回一个迭代器,表示无重复的值范围的结束。
words的大小并没有改变,依然保存着10个元素;只是这些元素的顺序改变了。unique实际上并没有删除任何元素,而是将无重复的元素复制到序列的前端,从而覆盖相邻的重复元素。
调用erase实现删除重复的项。这个函数调用从end_unique指向的元素开始删除,直到words的最后一个元素也删除掉为止。
bool isShorter(const string &s1,const string &s2) { return s1.size() < s2.size(); } bool GT6(const string &s) { return s.size() >= 6; } int main() { vector<string> words; ifstream inFile("input"); string word; while (inFile >> word) { words.push_back(word); } sort(words.begin(),words.end()); vector<string>::iterator end_unique =<span style="color:#ff0000;"> unique</span>(words.begin(),words.end()); words.<span style="color:#ff0000;">erase</span>(end_unique,words.end()); <span style="color:#ff0000;">stable_sort</span>(words.begin(),words.end(),<span style="color:#ff0000;">isShorter</span>); vector<string>::size_type wc = count_if(words.begin(),words.end(),GT6); cout << wc << " words 6 characters or longer." << endl; cout << "Words:" << endl; for (vector<string>::iterator it = words.begin(); it != words.end(); ++it) { cout << *it << endl; } }
除了sort之外,标准库还定义了stable_sort算法,stable_sort保留相等元素的原始相对位置。
sort和 stable_sort都是重载函数。其中一个版本使用元素类型提供的小于(<)操作符实现比较。第二个重载版本带有第三个形参:比较元素所使用的谓词函数的名字。这个谓词函数必须接受两个实参,实参的类型必须与元素类型相同,并返回一个可用作条件检测的值。
执行count_if函数时,首先读取他的头两个参数所标记的范围内的元素,每读取一个元素,则将其传递给第三个实参表示的谓词函数。