C++ Primer 学习笔记_40_STL实践与分析(14)--概要、先来看看算法【上】

STL实践与分析

--概述、初窥算法【上】

标准库容器定义的操作很少。并没有给容器加入大量的功能函数。而是选择提供一组算法,这些算法大都不依赖特定的容器类型,是“泛型”的。可作用在不同类型的容器和不同类型的元素上!

所谓泛型算法:一是由于它们实现共同的操作,所以称之为“算法”;而“泛型”指的是它们可以操作在多种容器类型上——不但可作用于vector或list这些标准库类型,还可用在内置数组类型、甚至其它类型的序列上,仅仅要自己定义的容器类型仅仅要与标准库兼容,同样可以使用这些泛型算法。

大多数算法是通过遍历由两个迭代器标记的一段元素来实现其功能。

典型情况下,算法在遍历一段元素范围时,操纵当中的每个元素。算法通过迭代器訪问元素,这些迭代器标记了要遍历的元素范围。

一、概述

    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;
    }

使用两个迭代器和一个值调用find函数,检查两个迭代器实參标记范围内的每个元素。仅仅要找到与给定值相等的元素,find就会返回指向该元素的迭代器。假设没有匹配的元素,find就返回它的第二个迭代器实參,表示查找失败。

由于find运算是基于迭代器的,因此可以在随意容器中使用同样的find函数查找值:

    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;
    }

除了容器类型与对象名称,差点儿没有不论什么改动。

相似的。由于指针的行为与作用在内置数组上的迭代器一样,因此也可以用find来搜索数组:

    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;
    }

假设须要一个子区间,则传递给这个子区间的第一个元素以及最后一个元素的下一位置的迭代器或指针。

    int *result = find(ia+2,ia+5,searchVal);

标准算法固有的独立于类型

这样的算法,正如我们所指出的,与容器的类型无关:在前面的描写叙述中,没有不论什么内容依赖于容器类型。这样的算法仅仅在一点上隐式地依赖元素类型:必须可以对元素做比較运算。

该算法的明白要求例如以下:

1)须要某种遍历集合的方式:可以从一个元素向前移到下一个元素。

2)必须可以知道是否到达了集合的末尾。

3)必须可以对容器中的每个元素与被查找的元素进行比較。

4)须要一个类型指出元素在容器中的位置,或者表示找不到该元素。

大多数情况下,每个算法都须要使用(至少)两个迭代器指出该算法操纵的元素范围。

第一个迭代器指向第一个元素,而第二个迭代器则指向最后一个元素的下一位置。第二个迭代器所指向的元素【超出末端迭代器】本身不是要操作的元素,而被用作终止遍历的哨兵。

假设元素类型不支持相等(==)操作符,或者打算用不同的測试方法来比較元素,则可使用第二个版本号的 find函数。这个版本号须要一个额外的參数:实现元素比較的函数名字。

这些算法从不使用容器操作,因而事实上现与类型无关,元素的全部訪问和遍历都通过迭代器实现。实际的容器类型未知(甚至所处理的元素是否存储在容器中也是未知的)。

//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;
    }
}
//习题11.2
int main()
{
    ifstream inFile("input");
    list<string> strList;
    string val;

    while (inFile >> val)
    {
        strList.push_back(val);
    }

    string searchVal;
    while (cin >> searchVal)
    {
        cout << searchVal << " have present "
             << count(strList.begin(),strList.end(),searchVal)
             << " times" << endl;
    }
}

【关键概念:算法永不运行容器提供的操作】

泛型算法本身从不运行容器操作,仅仅是单独依赖迭代器和迭代器操作实现。

算法基于迭代器及其操作实现,而并不是基于容器操作。

【P338推荐!】

二、初窥算法【上】

使用泛型算法必须包括algorithm头文件:

#include <algorithm>

标准库还定义了一组泛化的算术算法。其命名习惯与泛型算法同样,使用这些算法必须包括numeric头文件:

#include <numeric>

除了少数例外情况,全部算法都在一段范围内的元素上操作,我们将这段范围称为“输出范围”。

带有输入范围參数的算法总是使用头两个形參标记该范围。这两个形參是分别指向要处理的第一个元素和最后一个元素的下一位置的迭代器。

1、仅仅读算法

accumulate的使用:

一个简单的仅仅读算法accumulate,该算法在numeric头文件里定义。

    int sum = accumulate(iVec.begin(),iVec.end(),0);
    cout << sum << endl;

将sum设置为 vec的元素之和再加上0。accumulate带有三个形參。

头两个形參指定要累加的元素范围。

第三个形參则是累加的初值。

首先,调用该函数时必须传递一个起始值,否则,accumulate将不知道使用什么起始值。

其次,容器内的元素类型必须与第三个实參的类型匹配,或者可转换为第三个实參的类型。在accumulate内部,第三个实參用作累加的起点;容器内的元素按顺序连续累加到总和之中。

因此,必须可以将元素类型加到总和类型上。

相似的,也可以使用accumulate把string类型的vector容器中的元素连接起来:

string sum = accumulate(strVec.begin(),strVec.end(),string(""));

【注意】

程序显式地创建了一个string对象,用该函数调用的第三个实參。传递一个字符串字面值,将会导致编译时错误。由于此时,累加和的类型将是 constchar*,而 string的加法操作符所使用的操作数则各自是string和 constchar* 类型,加法的结果将产生一个string对象,而不是 constchar* 指针。

find_first_of的使用:

这个算法带有两对迭代器參数来标记两段元素范围,在第一段元素范围内查找与第二段范围中随意元素匹配的元素,然后返回一个迭代器,指向第一个匹配的元素。假设找不到匹配元素。则返回第一个范围的end迭代器。

//使用find_first_of统计有多少个数字在这两个容器中同一时候出现
    size_t cnt = 0;
    vector<int>::iterator iter = iVec1.begin();

    //在while的第一次循环中,遍历整个iVec1范围。<p style="margin: 10px auto; padding-top: 0px; padding-bottom: 0px;"></p><p style="margin: 10px auto; padding-top: 0px; padding-bottom: 0px;">
    //第二次以及兴许的循环迭代则仅仅考虑iVec1中尚未匹配的部分
    while ((iter = find_first_of(iter,iVec1.end(),iVec2.begin(),iVec2.end())) != iVec1.end())
    {
        cout << *iter << endl;
        ++ cnt;
        ++ iter;
    }
    cout << "cnt = " << cnt << endl;
</p>

【关键概念:迭代器实參类型,P340,值得细致品读】

//P341 习题11.3
int main()
{
    vector<int> iVec;
    ifstream inFile("input");
    int val;

    while (inFile >> val)
    {
        iVec.push_back(val);
    }

    int sum = accumulate(iVec.begin(),iVec.end(),0);
    cout << sum << endl;
}
时间: 2024-10-09 22:11:54

C++ Primer 学习笔记_40_STL实践与分析(14)--概要、先来看看算法【上】的相关文章

C++ Primer 学习笔记_45_STL实践与分析(19)--建筑常规算法

STL实践与分析 --泛型算法的结构 引言: 正如全部的容器都建立在一致的设计模式上一样,算法也具有共同的设计基础. 算法最主要的性质是须要使用的迭代器种类.全部算法都指定了它的每一个迭代器形參可使用的迭代器类型. 比方,假设形參必须为随机訪问迭代器则可提供vector或 deque类型的迭代器,或者提供指向数组的指针. 而其它容器的迭代器不能用在这类算法上. C++还提供了另外两种算法模式:一种模式由算法所带的形參定义;还有一种模式则通过两种函数命名和重载的规范定义. 一.算法的形參模式 大多

C++ Primer 学习笔记_45_STL实践与分析(19)--泛型算法的结构

STL实践与分析 --泛型算法的结构 引言: 正如全部的容器都建立在一致的设计模式上一样,算法也具有共同的设计基础. 算法最主要的性质是须要使用的迭代器种类.全部算法都指定了它的每一个迭代器形參可使用的迭代器类型.比方,假设形參必须为随机訪问迭代器则可提供vector或 deque类型的迭代器,或者提供指向数组的指针.而其它容器的迭代器不能用在这类算法上. C++还提供了另外两种算法模式:一种模式由算法所带的形參定义;还有一种模式则通过两种函数命名和重载的规范定义. 一.算法的形參模式 大多数的

C++ Primer 学习笔记_46_STL实践与分析(20)--容器特有的算法

STL实践与分析 --容器特有的算法 与其它顺序容器所支持的操作相比,标准库为list容器定义了更精细的操作集合,使它不必仅仅依赖于泛型操作.当中非常大的一个原因就是list容器不是依照内存中的顺序进行布局的,不支持随即訪问,这样,在list容器上就不能使用随即訪问迭代器的算法,如sort等:还有其它的一些算法如:merge.remove.reverse和unique,尽管能够用在list上,但却付出了高昂的性能代价.因此标准库结合list的内部结构,编写出了更快算法: list容器特有的操作

C++ Primer 学习笔记_35_STL实践与分析(9)--map种类(在)

STL实践与分析 --map类型(上) 引: map是键-值对的集合. map类型通常能够理解为关联数组:能够通过使用键作为下标来获取一个值,正如内置数组类型一样:而关联的本质在于元素的值与某个特定的键相关联,而并不是通过元素在容器中的位置来获取. 一.map对象的定义 1.定义map对象时,必须分别指明键和值的类型: map<string,int> wordCnt; map的构造函数 map<K,V>m; 创建一个名为m的空对象,其键和值的类型分别为K和V map<K,V&

C++ Primer 学习笔记_43_STL实践与分析(17)--再谈迭代器【中】

STL实践与分析 --再谈迭代器[中] 二.iostream迭代[续] 3.ostream_iterator对象和ostream_iterator对象的使用 能够使用ostream_iterator对象将一个值序列写入流中,其操作过程与使用迭代器将一组值逐个赋值给容器中的元素同样: ostream_iterator<string> out_iter(cout,"\n"); istream_iterator<string> in_iter(cin),eof; wh

C++ Primer 学习笔记_29_STL实践与分析(3) --操作步骤集装箱(下一个)

STL实践与分析 --顺序容器的操作(下) 六.訪问元素 假设容器非空,那么容器类型的front和back成员将返回容器的第一个和最后一个元素的引用. [与begin和end的对照:] 1)begin和end返回容器类型的迭代器,而不是引用: 2)end返回容器最后一个元素的下一个位置的迭代器,而back返回容器的最后一个元素的引用! /* *必须保证该list容器非空! *假设容器为空,则if语句内的全部操作都是没有定义的! */ if (!iList.empty()) { list<int>

C++ Primer 学习笔记_41_STL实践与分析(15)--先来看看算法【下一个】

STL实践与分析 --初窥算法[下] 一.写容器元素的算法 一些算法写入元素值.在使用这些算法写元素时一定要当心.必须确保算法所写的序列至少足以存储要写入的元素. 1.写入输入序列的元素 写入到输入序列的算法本质上是安全的--仅仅会写入与指定输入范围数量同样的元素. 写入到输入序列的一个简单算法是fill函数: fill(iVec.begin(),iVec.end(),10); fill(iVec.begin(),iVec.begin()+iVec.size()/2,0); fill带有一对迭代

C++ Primer 学习笔记_44_STL实践与分析(18)--再谈迭代器【下】

STL实践与分析 --再谈迭代器[下] 三.反向迭代器[续:习题] //P355 习题11.19 int main() { vector<int> iVec; for (vector<int>::size_type index = 0; index != 10; ++index) { iVec.push_back(index); } for (vector<int>::reverse_iterator r_iter = iVec.rbegin(); r_iter !=

C++ Primer 学习笔记_42_STL实践与分析(16)–再谈迭代器【上】

STL实践与分析 --再谈迭代器[上] 引言: 另外三种迭代器类型: 1)插入迭代器:这类迭代器与容器绑定在一起,实现在容器中插入元素的功能. 2)iostream迭代器:这类迭代器可以与输入与输出流绑定在一起,用于迭代遍历所关联的IO流. 3)反向迭代器:这类迭代器实现向后遍历,而不是向前遍历,所有的容器都定义了自己的reverse_iterator类型,由rbegin和rend成员函数返回. 上述迭代器都在iterator头文件中定义. 一.插入迭代器 前面曾经提到的back_inserte