泛型算法 - 1【C++ Primer 学习笔记 - 第十一章】

来源:互联网 发布:档案管理系统软件源码 编辑:程序博客网 时间:2024/04/30 01:46
标准容器定义了很少的操作,如添加、删除元素,访问第一个、最后一个元素,获取容器大小等。
但是,用户可能需要其他更多的有用操作,如:排序、查找、查找最大元素、查找最小元素等,
为了应对这种需要,
标准库并没有为每种容器类型都定义实现相应的成员函数,而是定义了一组泛型算法
因为他们实现共同的操作,因此,称为算法
所谓泛型,指的是,它们可以操作在多种容器类型上,如标准库类型 vector、 list ,内置数组类型、甚至其他类型的序列。

自定义类型,只要与标准库兼容,同样可以使用这些泛型算法。


#include <algorithm>#include <numeric>


//using std::list;//using std::find_first_of;int search_value = 42;vector<int> ivec;ivec.push_back(42);ivec.push_back(10);vector<int>::const_iterator iter = find(ivec.begin(), ivec.end(), search_value);cout << "The value " << search_value<< (iter == ivec.end() ? " is not present." : " is present.") << endl;int ia[6] = {28, 283, 48, 42, 42, 90};int *pint = find(ia, ia+6, search_value);// accumulate:累加求和// 第三个参数,是累加的初值// 返回类型,即是第三个实参的类型// 元素类型必须与第三个实参类型兼容int isum = accumulate(ia, ia+6, 1);vector<string> svec;svec.push_back("a");svec.push_back("b");string ssum = accumulate(svec.begin(), svec.end(), string(""));cout << "The value " << search_value<< (pint == ia+6 ? " is not present." : " is present.") << endl;cout << "int sum : " << isum << endl;cout << "string sum : " << ssum << endl;vector<string> svecUsers;list<char *> clistAdmins;svecUsers.push_back("Xiaoming");svecUsers.push_back("Zhangsan");svecUsers.push_back("Ligang");clistAdmins.push_back("Xiaoming");clistAdmins.push_back("Ligang");vector<string>::iterator iter2 = svecUsers.begin();size_t cnt = 0;// 此处之所以没有将 iter2 定义为 const_iterator// 是因为:svecUsers.end() 的返回类型,是根据 svecUsers 的类型而定的// svecUsers 如果是 const 对象,则,返回 const_iterator 类型,// 否则,返回 iterator 类型。// 而 find_first_of 算法,要求指定范围的两个迭代器必须具有完全一致的类型,// svecUsers 不是 const 对象while((iter2 = find_first_of(iter2, svecUsers.end(),        clistAdmins.begin(), clistAdmins.end())) != svecUsers.end()){++cnt;++iter2;}cout << "Count: " << cnt << endl;
find_first_of , 在第一个范围内查找,返回第一个同时存在于第一、第二范围内的元素。
find、 find_first_of 这类,属于只读算法。

算法固有地独立于类型
大多数情况下,每个算法都需要使用(至少)两个迭代器来指出该算法操纵的元素范围。
find 操作要求元素类型定义了相等(==)操作符,来比较元素。
否则,可使用 find 的重载版本,需要提供一个额外参数:实现元素比较的函数名字

算法从不使用容器操作,因而其实现与类型无关,元素的所有访问、遍历都是通过迭代器实现。
算法使用“普通”迭代器时,有可能会改变元素的值,移动元素,但是,从不修改基础容器的大小
使用了插入器(inserter)这类特殊迭代器的时候,也只会通过插入器来添加元素,
而不会由算法直接添加。

绝大多数算法,都在一段范围内的元素上操作,称为:输入范围(Input Range)
带有输入范围的算法总是使用头两个形参标记该范围。



写容器元素的算法

vector<int> ivec(10,8);// 前5个,置0fill(ivec.begin(), ivec.begin() + ivec.size()/2, 0);//vector<int> ivec2;//fill_n(ivec2.begin(), 10, 0);//以上会报错。 //fill_n 不会检查目标大小是否足够存储要写入的元素// 用迭代器给元素赋值,被赋值的是迭代器所指向的元素// 用插入迭代器赋值,会在容器中添加一个新元素// #include <iterator>// back_inserter:迭代器适配器// 与容器适配器一样,迭代器适配器使用一个对象作为实参,// 并生成一个适应其实参行为的新对象。// 以下例子,back_inserter 生成一个插入迭代器。// 试图通过这个迭代器给元素赋值时,// 将调用 push_back 在容器中添加一个具有指定值的元素vector<int> ivec2;fill_n(back_inserter(ivec2), 10, 9);list<int> ilist(8, 9);// 以下效率不高,等价于: // vector<int> ivec3(ilist.begin(), ilist.end())vector<int> ivec3;copy(ilist.begin(), ilist.end(), back_inserter(ivec3));



算法的 _copy 版本


list<int> ilist(10, 5);replace(ilist.begin(), ilist.end(), 5, 9);vector<int> ivec;// replace_copy 会创建副本,替换后,存入 ivecreplace_copy(ilist.begin(), ilist.end(), back_inserter(ivec), 9, 8);cout << *ilist.begin() << endl; // 9cout << *ivec.begin() << endl;  // 8


// 谓词 predicate// 谓词是做某些检测的函数,判断条件是否成立bool isShorter(const string &s1, const string &s2){return s1.size() < s2.size();}bool GT6(const string &s){return s.size() >=6;}string make_plural(size_t ctr, const string &word, const string &ending){return (ctr==1) ? word : word + ending;}


// 以下代码,实现功能:// 计算输入的单词中,长度 >= 6 的单词个数// 不包括重复词vector<string> svec;string temp_word;// Ctrl + Z 结束 cinwhile(cin >> temp_word){svec.push_back(temp_word);}// sort 进行排序,相同的元素就会被排到相邻的位置sort(svec.begin(), svec.end());// unique 算法,“删除”相邻的重复元素,实际上是重新排序// 返回的迭代器指向,超出无重复的元素范围末端的下一位置vector<string>::iterator end_unique = unique(svec.begin(), svec.end());// 调用容器操作,来真正删除元素(算法不直接删除容器元素)svec.erase(end_unique, svec.end());// 按照长度排序,相同长度,仍然保持字典顺序// isShorter 谓词函数,必须接收两个实参,类型与元素类型相同,// 并返回一个可用于条件检测的值stable_sort(svec.begin(), svec.end(), isShorter);vector<string>::size_type wc = count_if(svec.begin(), svec.end(), GT6);cout << wc << " " << make_plural(wc, "word", "s") << " 6 characters or longer" << endl;


C++ 提供的另外3种迭代器:插入迭代器(insert iterator)、iostream迭代器(iostream iterator)、反向迭代器(reverse iterator)
在 iterator 头文件中定义

插入器,是一种迭代器适配器。带有一个容器参数,并生成一个迭代器,用于在指定容器中插入元素。
三种插入器:back_inserter、front_inserter、inserter。

front_insert 需要使用 push_front ,因此容器需要提供 push_front 操作,像 vector 这类不支持 push_front 操作的容器,则无法使用 front_inserter

list<int> ilist;ilist.push_back(20);ilist.push_back(1);vector<int> ivec;ivec.push_back(10);ivec.push_back(5);list<int>::iterator iter = find(ilist.begin(), ilist.end(), 20);// 将 ivec 中的 10 替换成 8,// 创建相应副本,插入到 ilist 的 iter 指向的位置前面replace_copy(ivec.begin(), ivec.end(),inserter(ilist, iter), 10, 8);


list<int> ilist, ilist2, ilist3, ilist4;// ilist: 3, 2, 1, 0for(list<int>::size_type i=0; i!=4; ++i){ilist.push_front(i);}// ilist2: 0, 1, 2, 3copy(ilist.begin(), ilist.end(), front_inserter(ilist2));// ilist3: 3, 2, 1, 0// 元素在ilist3 的固定位置插入// 该位置,插入第一个元素之前,是头部,// 插入一个元素之后,就不是头部了。copy(ilist.begin(), ilist.end(), inserter(ilist3, ilist3.begin()));// ilist4: 3, 2, 1, 0copy(ilist.begin(), ilist.end(), back_inserter(ilist4));list<int>::iterator iter = ilist4.begin();



iostream 迭代器

iostream 类型不是容器,但标准库同样提供了在 iostream 对象上使用的迭代器
istream_iterator 用于读取输入流, ostream_iterator 用于写输出流

将对应的流,视为特定类型的元素序列。
流迭代器只定义了基本的迭代操作:自增、解引用、赋值。
istream 迭代器可以比较是否相等;
ostream 迭代器不提供比较运算

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_iterator 的操作
it1 == it2
 
比较两个 istream_iterator 对象是否相等。迭代器读取的必须是相同的类型

it1 != it2
如果两个迭代器都是 end 值,则相等。
对于两个都不指向流结束位置的迭代器,如果它们使用同一个流输入构造,则它们也相等

*it
返回从流中读取的值

it->mem
相当于:(*it).mem。 返回从流中读取的对象的 mem 成员

++it
it++

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



原创粉丝点击