C++程序设计:原理与实践(进阶篇)16.2 最简单的算法f?ind()

16.2 最简单的算法f?ind()


f?ind()可能是最简单但又很有用的算法,它在一个序列中查找一个给定值:

 

让我们看看f?ind()的定义。你自然可以无须了解f?ind()的确切实现细节就使用它——实际上,我们已经在前面的章节中使用过f?ind()了(例如15.6.2节)。但是,f?ind()的定义展示了很多有用的设计思想,因此了解其实现是有价值的。

首先,f?ind()对一个序列进行操作,这个序列由一对迭代器定义。它在半开区间[f?irst: last)中查找给定值val。返回结果是一个迭代器,要么指向val在序列中首次出现的位置,要么就是last。在STL中,返回指向尾后位置的迭代器(last)是最常用的报告“未找到”的方法。因此,我们可以像下面这样使用f?ind():

 

在本例中,序列照例由一个容器(STL vector)中所有元素组成。我们检查返回的迭代器是否指向序列的结束,来判断是否找到了想要的值。返回值类型与迭代器参数的类型是一致的。

为了避免明确指明返回类型,我们使用了auto。若对象的“类型”定义为auto,则它从其初始化器获取类型。例如:

 

类型说明符auto在泛型编程中特别有用,例如在f?ind()中明确指明真实类型(本例中是vector<int>::iterator)是很烦人的。

我们现在已经知道如何使用f?ind()了,从而也了解了如何使用那些采用相同规范的算法。在学习更多用法和更多算法之前,让我们再进一步观察f?ind()的定义:

 

当你第一次看这段代码时,你会注意代码中的循环吗?我们不会。这个循环真的非常简洁、高效,并且直接表达了基础算法。然而,可能在看了若干例子之后,你才会注意到这个循环。让我们用比较常见的方式重写f?ind(),并比较两个版本:

 

这两个定义在逻辑上是等价的,而且一个真正优秀的编译器能够为它们生成相同的代码。然而,现实中很多编译器都没有那么好,它们无法消除额外的变量(p),也不能重排代码以使所有条件测试都在同一位置执行。我们为什么要为此担忧并加以解释呢?一部分原因在于f?ind()的第一个版本(也是应该优选的版本)的风格已变得十分流行,我们必须学会它以阅读他人编写的代码;另一部分原因是,对于用来处理大量数据且被频繁使用的小函数而言,性能是十分重要的。

试一试

你确定这两个定义在逻辑上是等价的吗?你是如何确定的?试着给出两者等价的论证。然后,用一些数据测试这两个定义。著名的计算机科学家Don Knuth曾经说,“我只是证明了算法的正确性,并没有对它进行测试”。即使是数学证明也可能包含错误。为了证明你的观点,推理和测试缺一不可。

16.2.1 一些一般的应用

f?ind()算法是通用的。这意味着它能用于不同的数据类型。实际上,f?ind()算法的通用性包括两个方面;它能用于:

任何STL风格的序列;

任何元素类型。

下面是一些例子(如果你感到困惑,请参考15.4节中的图表):

 

 

在此例中,f?ind()使用了vector<int>::iterator实现遍历操作;即,++f?irst中的++只是将指针移动到内存中的下一个位置(存储了vector的下一个元素),而*f?irst中的*对该指针进行解引用。迭代器比较(f?irst != last)就是指针的比较,而值的比较(*f?irst != val)就是比较两个整数。

让我们再尝试list:

 

在本例中,f?ind()使用了list<string>::iterator实现遍历操作。用到的运算符都具有符合要求的喻意,因此代码逻辑与vector<int>的例子相同。但实现是很不同的;即,++f?irst中的++简单地顺着元素的Link部分中的指针指向list下一元素的存储位置,而*f?irst中的*将获得Link的值部分。迭代器比较(f?irst != last)是Link *指针的比较,而值的比较(*f?irst != val)则是使用string的!=运算符比较两个string。

因此,f?ind()是极为灵活的:只要我们遵循了迭代器的简单规则,就可以用f?ind()在我们能想象的任何序列和能定义的任何容器中查找元素。例如,我们可以使用f?ind()在15.6节中定义的Document中查找一个字符:

 

这种灵活性是STL算法的标志性特点,也使得它们远比初次接触的人所能想象的更为有用。

时间: 2024-09-13 23:05:31

C++程序设计:原理与实践(进阶篇)16.2 最简单的算法f?ind()的相关文章

c++-关于《C++程序设计原理与实践》第3章例子的一个问题

问题描述 关于<C++程序设计原理与实践>第3章例子的一个问题 本人菜鸟,现正在学习C++.<C++程序设计原理与实践>第3章有一个例子,代码如下: #include #include #include #include #include using namespace std; inline void keep_window_open(){ char ch; cin >> ch; } int main() //C++ Programs start by executi

源代码-C++程序设计原理与实践

问题描述 C++程序设计原理与实践 #include "std_lib_facilities.h" int main() { cout<<"Hello,world!n"; return 0; } 我下了源代码,放到那里才能猜VC98编译时不出错?最好详细点,带有图解 解决方案 ...大哥,都什么年代了还用98

《 C++程序设计:原理与实践(进阶篇.》导读

本节书摘来自华章出版社< C++程序设计:原理与实践(进阶篇)>一书中作者[美] 本贾尼·斯特劳斯特鲁普(Bjarne Stroustrup) 著 刘晓光 李忠伟 王刚 译     前 言 Programming: Principles and Practice Using C++, Second Edition 该死的鱼雷!全速前进. --Admiral Farragut 程序设计是这样一门艺术,它将问题求解方案描述成计算机可以执行的形式.程序设计中很多工作都花费在寻找求解方案以及对其求精上

C++程序设计:原理与实践(进阶篇)16.9 容器算法

16.9 容器算法 到目前为止,我们都是用元素序列来定义标准库算法.序列用迭代器指明:一个输入序列定义为一对迭代器[b:e),其中b指向序列首元素,e指向序列尾元素之后位置(见15.3节).一个输出序列简单地用一个迭代器指定,该迭代器指向序列的首元素.例如:   这种方式很好.也很通用.例如,我们可以排序vector的一半内容:   但是,指明元素范围有些啰嗦,而大多数情况下,我们需要排序整个vector而不是一半.因此,大多数情况下,我们希望这样编写代码:   标准库未提供sort()的这种变

C++程序设计:原理与实践(进阶篇)16.8 排序和搜索

16.8 排序和搜索 我们经常希望自己的数据是有序的.为达到这个目的,我们可以使用一个能维护顺序的数据结构,例如map或set,或进行排序.在STL中,最常见和有用的排序操作是sort(),我们已经使用过多次了.在默认情况下,sort()使用<作为排序标准,但是我们也可以提供自己的标准:   作为一个基于用户指定规则进行排序的例子,我们将介绍如何进行不考虑大小写的字符串排序:     一旦一个序列已排序,我们不再需要用f?ind()从开始位置进行搜索:我们可以利用这种序进行二分搜索.二分搜索的基

C++程序设计:原理与实践(进阶篇)16.1 标准库算法

摘要 Programming: Principles and Practice Using C++, Second Edition 算法和映射 理论上,实践是简单的. --Trygve Reenskaug 本章将完成我们对STL基本思想的介绍以及对STL所提供工具的纵览.在本章中,我们主要关注算法.我们的主要目的是给你介绍一些最有用的算法,它们能够节省你大量时间,即使达不到以月计,也能达到以天计.每个算法都将通过其使用示例和支持的编程技术来介绍.本章的另一个目的是提供足够的工具,令你在需要标准库

C++程序设计:原理与实践(进阶篇)16.6 关联容器

16.6 关联容器 除了vector之外,最有用的标准库容器恐怕就是map了.一个map就是一个(键,值)对的有序序列,你可以基于一个关键字在其中查找对应的值:例如my_phone_book["Nicholas"]应该是Nicholas的电话号码.在流行度的竞争中,map唯一的潜在竞争对手是unordered_map(见16.6.4节),它是一种针对字符串关键字优化过的map.类似map和unordered_map的数据结构有很多名字,例如关联数组(associative array)

C++程序设计:原理与实践(进阶篇)16.7 拷贝

16.7 拷贝 在16.2节中,我们认为f?ind()是"最简单的有用算法".当然,这一点可以讨论.很多简单算法都是有用的--甚至其中有些编写起来有些过于简单了.当你可以使用其他人编写和调试好的代码时,为什么要费力编写新的代码?当谈及简单性和有效性时,copy()可以与f?ind()媲美.STL提供了三个版本的拷贝: 拷贝操作 copy(b,e,b2) 将[b:e)拷贝到[b2:b2+(e-b)) unique_copy(b,e,b2) 将[b:e)拷贝到[b2:b2+(e-b)),禁

C++程序设计:原理与实践(进阶篇)16.3 通用搜索算法f?ind_if()

16.3 通用搜索算法f?ind_if() 其实我们并没有那么经常地需要查找一个特定值.我们通常更感兴趣的是在序列中查找符合某种标准的值.如果能够允许我们自己定义查找标准,这样的f?ind操作就更为有用.例如,我们也许希望查找大于42的值,也许希望在不考虑大小写的情况下比较字符串,也许希望找到第一个奇数值,也许希望查找一个地址域值为"17 Cherry Tree Lane"的记录. 根据用户提供的标准进行查找的标准算法是f?ind_if():   显然(当你比较源码时),f?ind_i