关闭

笔记5 (泛型算法)

296人阅读 评论(0) 收藏 举报
分类:

泛型算法

泛型算法(genericalgorithm)因为它们实现共同的操作,所以称这为“算法”;而“泛型”指的是它们可以操作在多种容器类型上。

每个泛型算法的实现都独立于单独的容器。

 

初窥算法

#include<algorithm>

#include<numeric>

除了少数例外情况,所有算法都在一段范围内的元素上操作,我们将这段称为“输入范围”。带有输入范围参数的算法总是使用头两个形参标记该范围。这两个形参是分别指向要处理的第一个元素和最后一个元素的下一位置的迭代器。

根据对元素的操作将算法分为下面几种:

只读算法,不改变元素的值顺序。

给指定元素赋新值的算法。

将一个元素的值移给另一个元素的算法

 

只读算法

         许多算法只会读取其输入范围内的元素,而不会写这些元素。find 就是一个这样的算法。另一个简单的只读算法是 accumulate,该算法在 numeric 头文件中定义。

         除了 find 之外,标准库还定义了其他一些更复杂的查找算法。当中的一部分类似 string 类的 find 操作,其中一个是 find_first_of 函数。这个算法带有两对迭代器参数来标记两段元素范围,在第一段范围内查找与第二段范围中任意元素匹配的元素,然后返回一个迭代器,指向第一个匹配的元素。如果找不到元素,则返回第一个范围的 end 迭代器。

 

写容器元素的算法

         有些算法直接将数据写入到输入序列,另外一些则带有一个额外的迭代器参数指定写入目标。这类算法将目标迭代器用作输出的位置。还有第三种算法将指定数目的元素写入某个序列。

 

写入输入序列的元素

写入到输入序列的算法本质上是安全的——只会写入与指定输入范围数量相同的元素。

写入到输入序列的一个简单算法是 fill 函数

 

不检查定写入操作的算法

         fill_n 函数带有的参数包括:一个迭代器、一个计数器以及一个值。该函数从迭代器指向的元素开始,将指定数量的元素设置为给定的值。fill_n 函数假定对指定数量的元素做写操作是安全的。初学者常犯的错误的是:在没有元素的空容器上调用 fill_n 函数(或者类似的写元素算法)。

         对指定数目的元素做写入运算,或者写到目标迭代器的算法,都不检查目标的大小是否足以存储要写入的元素。

 

引入back_inserter

         确保算法有足够的元素存储输出数据的一种方法是使用插入迭代器(insert iterator)。插入迭代器是可以给基础容器添加元素的迭代器。

         back_itserter函数是迭代器的适配器。迭代器适配器使用一个对象作为,并生成一个适应其实参行为的新对象。

 

写入到目标迭代器的算法

         第三类算法向目标器迭代写入未知个数的元素。目标迭代器指向存入输出数据的序列第一个元素。

 

算法的_copy版本

         有些算法提供所谓的“复制(copying)”版本。这些算法对输入序列的元素做出处理,但不修改原来的元素,而是创建一个新序列存储元素的处理结果。

 

对容器元素重新排序的算法

sort算法带有两个迭代器实参,指出要排序的元素范围。这个算法使用小于(<)操作符比较元素。

unique算法带有两个指定元素范围的迭代器参数,该算法删除相邻的重复元素(并未真正删除重复元素,只是放到容器尾部,若要删除,使用容器操作),然后重新排列输入范围内的元素,并且返回一个迭代器,表示无重复的值范围的结束。

谓词(predicate)是做某些检测的函数,返回用于条件判断的类型,指出条件是否成立。

stable_sort算法保留相等元素的原始相对位置,其第三个形参:比较元素所使用的谓词函数的名字。这个谓词函数必须接受两个实参,实参的类型必须与元素类型相同,并返回一个可用作条件检测的值。

count_if算法首先读取它的头两个实参所标记的范围内的元素。每读出一个元素,就将它给第三个实参表示的谓词函数。此谓词函数需要单个元素类型的实参,并返回一个可用作条件检测的值。count_if算法返回使谓词函数返回条件成立的元素个数。

 

再谈迭代器

 

插入迭代器

插入器是一种迭代器适配器,带有一个容器参数,并生成一个迭代器,用于在指定容器中插入元素。通过插入迭代器赋值时,迭代器会插入一个新的元素。

(1)back_inserter,创建使用push_back实现插入的迭代器。

(2)front_interster,使用push_front实现插入。

(3)inserter,使用insert实现插入操作。除了所关联的容器外,inserter还带有第二实参:指向插入起始位置的迭代器。

iostream迭代器

istream_iterator<T> in(strm);

创建从输入流 strm 中读取 T 类型对象的 istream_iterator 对象

istream_iterator<T> in;

istream_iterator 对象的超出末端迭代器

ostream_iterator<T> in(strm);

创建将 T 类型的对象写到输出流 strm 的 ostream_iterator 对象

 ostream_iterator<T> in(strm, delim);

创建将 T 类型的对象写到输出流 strm 的 ostream_iterator 对象,在写入过程中使用 delim 作为元素的分隔符。delim 是以空字符结束的字符数组

         流迭代器只定义了最基本的迭代器操作:自增、解引用和赋值。此外,可比较两个istream迭代器是否相等(或不等)。而ostream迭代器则不提供比较运算。

it1 == it2 it1 != it2

比较两上 istream_iterator 对象是否相等(不等)。迭代器读取的必须是相同的类型。如果两个迭代器都是 end 值,则它们相等。对于两个都不指向流结束位置的迭代器,如果它们使用同一个输入流构造,则它们也相等

*it

返回从流中读取的值

it->mem

是 (*it).mem 的同义诩。返回从流中读取的对象的 mem 成员

++it it++

通过使用元素类型提供的 >> 操作从输入流中读取下一个元素值,使迭代器向前移动。通常,前缀版本使用迭代器在流中向前移动,并返回对加 1 后的迭代器的引用。而后缀版本使迭代器在流中向前移动后,返回原值

 

反向迭代器

         反向迭代器是一种反向遍历容器的迭代器。也就是,从最后一个元素到第一个元素遍历容器。反向迭代器将自增(和自减)的含义反过来了:对于反向迭代器,++运算将访问前一个元素,而--运算则访问下一个元素。

 

五种迭代器

Input iterator(输入迭代器)

读,不能写;只支持自增运算

Output iterator(输出迭代器)

写,不能读;只支持自增运算

Forward iterator(前向迭代器)

读和写;只支持自增运算

Bidirectional iterator(双向迭代器)

读和写;支持自增和自减运算

Random access iterator(随机访问迭代器)

读和写;支持完整的迭代器算术运算

 

因为关联容器的键是const对象。因此,关联容器不能使用任何写序列元素的算法。只能使用关联容器绑在一起的迭代器来提供用于读操作的实参。

 

泛型算法的结构

 

算法的形参模式

大多数算法采用下面四种形式之一:

     alg (beg, end,other parms);

     alg (beg, end,dest, other parms);

     alg (beg, end,beg2, other parms);

     alg (beg, end,beg2, end2, other parms);

其中,alg 是算法的名字,beg 和 end 指定算法操作的元素范围。我们通常将该范围称为算法的“输入范围”。尽管几乎所有算法都有输入范围,但算法是否使用其他形参取决于它所执行的操作。这里列出了比较常用的其他形参:dest、beg2 和 end2,它们都是迭代器。这些迭代器在使用时,充当类似的角色。除了这些迭代器形参之外,有些算法还带有其他的菲迭代器形参,它们是这些算法特有的。

 

算法的命名规范

1、区别带有一个值或一个谓词函数参数的算法版本。带有谓词函数形参的算法,其名字逞有后缀_if。

2、区别是否实现复制的算法版本。此版本的算法在名字中添加了_copy后缀。

 

容器特有的算法

 

list容器上的迭代器是双向的,而不是随机访问类型。由于list容器不支持随机访问,因此,在此容器上不能使用需要随机访问迭代器的算法。这些算法包括sort及其相关的算法。还有一些其他泛型算法,如merge、remove、reverse和unique,虽然可以用在list上,但却付出了性能上的代价。如果这些算法利用list容器实现的特点,则可以更高效地执行。

 lst.merge(lst2) lst.merge(lst2, comp)

将 lst2 的元素合并到 lst 中。这两个 list 容器对象都必须排序。lst2 中的元素将被删除。合并后,lst2 为空。返回 void 类型。第一个版本使用 < 操作符,而第二个版本则使用 comp 指定的比较运算

 lst.remove(val) lst.remove_if(unaryPred)

调用 lst.erase 删除所有等于指定值或使指定的谓词函数返回非零值的元素。返回 void 类型

lst.reverse()

反向排列 lst 中的元素

lst.sort

对 lst 中的元素排序

lst.splice(iter, lst2)

lst.splice(iter, lst2, iter2)

lst.splice(iter, beg, end)

将 lst2 的元素移到 lst 中迭代器 iter 指向的元素前面。在 lst2 中删除移出的元素。第一个版本将 lst2 的所有元素移到 lst 中;合并后,lst2 为空。lst 和 lst2 不能是同一个 list 对象。第二个版本只移动 iter2 所指向的元素,这个元素必须是 lst2 中的元素。在这种情况中,lst 和 lst2 可以是同一个 list 对象。也就是说,可在一个 list 对象中使用 splice 运算移动一个元素。第三个版本移动迭代器 beg 和 end 标记的范围内的元素。beg 和 end 照例必须指定一个有效的范围。这两个迭代器可标记任意 list 对象内的范围,包括 lst。当它们指定 lst 的一段范围时,如果 iter 也指向这个范围的一个元素,则该运算未定义。

 lst.unique() lst.unique(binaryPred)

调用 erase 删除同一个值的团结副本。第一个版本使用 == 操作符判断元素是否相等;第二个版本则使用指定的谓词函数实现判断

 

 

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:4690次
    • 积分:140
    • 等级:
    • 排名:千里之外
    • 原创:9篇
    • 转载:7篇
    • 译文:0篇
    • 评论:0条
    文章分类
    文章存档