复习 泛型编程与STL 下

来源:互联网 发布:淘宝古装哪家比较好的 编辑:程序博客网 时间:2024/06/09 23:20

5、关联容器

5.1  分类与基本功能








四种关联容器
•单重关联容器(set和map)
▫键值是唯一的,一个键值只能对应一个元素
•多重关联容器(multiset和multimap)
▫键值是不唯一的,一个键值可以对应多个元素
•简单关联容器(set和multiset)
▫容器只有一个类型参数,如set<K>、multiset<K>,表示键类型
▫容器的元素就是键本身
•二元关联容器(map和multimap)
▫容器有两个类型参数,如map<K,V>、multimap<K,V>,分别表示键和附加数据的类型
▫容器的元素类型是pair<K,V>,即由键类型和元素类型复合而成的二元组

无序关联容器
•C++11新标准中定义了4个无序关联容器
▫unordered_set、unordered_map、unordered_multiset、unordered_multimap
•不是使用比较运算符来组织元素的,而是通过一个哈希函数和键类型的==运算符。
•提供了与有序容器相同的操作
•可以直接定义关键字是内置类型的无序容器。
•不能直接定义关键字类型为自定义类的无序容器,如果需要,必须提供我们自己的hash模板

5.2 集合(set)

•集合用来存储一组无重复的元素。由于集合的元素本身是有序的,可以高效地查找指定元素,也可以方便地得到指定大小范围的元素在容器中所处的区间。

•例10-9 输入一串实数,将重复的去掉,取最大和最小者的中值,分别输出小于等于此中值和大于等于此中值的实数

//10_9.cpp#include <set>#include <iterator>#include <utility>#include <iostream>using namespace std;int main() {<span style="white-space:pre">set<double> s;while (true) {double v;cin >> v;if (v == 0) break;//输入0表示结束//尝试将v插入pair<set<double>::iterator,bool> r=s.insert(v);if (!r.second)//如果v已存在,输出提示信息cout << v << " is duplicated" << endl;}//得到第一个元素的迭代器set<double>::iterator iter1=s.begin();//得到末尾的迭代器set<double>::iterator iter2=s.end();//得到最小和最大元素的中值double medium=(*iter1 + *(--iter2)) / 2;//输出小于或等于中值的元素cout<< "<= medium: "copy(s.begin(), s.upper_bound(medium), ostream_iterator<double>(cout, " "));cout << endl;//输出大于或等于中值的元素cout << ">= medium: ";copy(s.lower_bound(medium), s.end(), ostream_iterator<double>(cout, " "));cout << endl;return 0;</span><span style="font-family: Arial, Helvetica, sans-serif;">}</span>

5.3 映射 map

•映射与集合同属于单重关联容器,它们的主要区别在于,集合的元素类型是键本身,而映射的元素类型是由键和附加数据所构成的二元组。
•在集合中按照键查找一个元素时,一般只是用来确定这个元素是否存在,而在映射中按照键查找一个元素时,除了能确定它的存在性外,还可以得到相应的附加数据。

•例10-10 有五门课程,每门都有相应学分,从中选择三门,输出学分总和

//10_10.cpp#include <iostream>#include <map>#include <string>#include <utility>using namespace std;int main() {map<string, int> courses;//将课程信息插入courses映射中courses.insert(make_pair("CSAPP", 3));courses.insert(make_pair("C++", 2));courses.insert(make_pair("CSARCH", 4));courses.insert(make_pair("COMPILER", 4));courses.insert(make_pair("OS", 5));int n = 3;//剩下的可选次数int sum = 0;//学分总和while (n > 0) {string name;cin >> name;//输入课程名称map<string, int>::iterator iter = courses.find(name);//查找课程if (iter == courses.end()) {//判断是否找到cout << name << " is not available" << endl;} else {sum += iter->second;//累加学分courses.erase(iter);//将刚选过的课程从映射中删除n--;}}cout << "Total credit: " << sum << endl;//输出总学分return 0;}

•例10-11统计一句话中每个字母出现的次数

// 10_11.cpp#include <iostream>#include <map>#include <cctype>using namespace std;int main() {map<char, int> s;//用来存储字母出现次数的映射char c;//存储输入字符do {cin >> c;//输入下一个字符if (isalpha(c)){ //判断是否是字母c = tolower(c); //将字母转换为小写s[c]++;//将该字母的出现频率加1}} while (c != '.'); //碰到“.”则结束输入//输出每个字母出现次数for (map<char, int>::iteratoriter= s.begin(); iter!= s.end(); ++iter)cout<< iter->first << " " << iter->second << " ";cout<< endl;return 0;}

5.4 多重集合(multiset)与多重映射(multimap)

•多重集合是允许有重复元素的集合,多重映射是允许一个键对应多个附加数据的映射。
•多重集合与集合、多重映射与映射的用法差不多,只在几个成员函数上有细微差异,其差异主要表现在去除了键必须唯一的限制。

•例10-12 上课时间查询

//10_12.cpp#include <iostream>#include <map>#include <utility>#include <string>using namespace std;int main() {multimap<string, string> courses;typedef multimap<string, string>::iterator CourseIter;//将课程上课时间插入courses映射中courses.insert(make_pair("C++", "2-6"));courses.insert(make_pair("COMPILER", "3-1"));courses.insert(make_pair("COMPILER", "5-2"));courses.insert(make_pair("OS", "1-2"));courses.insert(make_pair("OS", "4-1"));courses.insert(make_pair("OS", "5-5"));//输入一个课程名,直到找到该课程为止,记下每周上课次数string name;int count;do {cin >> name;count = courses.count(name);if (count == 0)cout << "Cannot find this course!" << endl;} while (count == 0);//输出每周上课次数和上课时间cout << count << " lesson(s) per week: ";pair<CourseIter, CourseIter> range = courses.equal_range(name);for (CourseIter iter = range.first; iter != range.second; ++iter)cout << iter->second << " ";cout << endl;return 0;}

6.函数对象



•函数对象
▫一个行为类似函数的对象
▫可以没有参数,也可以带有若干参数
▫其功能是获取一个值,或者改变操作的状态。

▫普通函数就是函数对象
▫重载了“()”运算符的类的实例是函数对象


例10-13、例10-14
•使用两种方式定义表示乘法的函数对象
▫通过定义普通函数(例10-13)
▫通过重载类的“()”运算符(例10-14)
•用到以下算法:template<class InputIterator, class Type, class BinaryFunction>Type accumulate(InputIterator first, InputIterator last, Type val, BinaryFunction binaryOp);
▫对[first, last)区间内的数据进行累“加”,binaryOp为用二元函数对象表示的“加”运算符,val为累“加”的初值

#include <iostream>#include <numeric> //包含数值算法头文件using namespace std;//定义一个普通函数intmult(intx, inty) { return x * y; };intmain() {inta[] = { 1, 2, 3, 4, 5 };const intN = sizeof(a) / sizeof(int);cout<< "The result by multiplingall elements in a is "<< accumulate(a, a + N, 1, mult)<< endl;return 0;}//10_14.cpp#include <iostream>#include <numeric> //包含数值算法头文件using namespace std;class MultClass{ //定义MultClass类public://重载操作符operator()intoperator() (intx, inty) const { return x * y; }};intmain() {inta[] = { 1, 2, 3, 4, 5 };const intN = sizeof(a) / sizeof(int);cout<< "The result by multiplingall elements in a is "<< accumulate(a, a + N, 1, MultClass()) //将类multclass传递给通用算法<< endl;return 0;}

STL提供的函数对象
•用于算术运算的函数对象
•用于关系运算、逻辑运算的函数对象(要求返回值为bool)

•用于算术运算的函数对象:
▫一元函数对象(一个参数) :negate
▫二元函数对象(两个参数) :plus、minus、multiplies、divides、modulus
•用于关系运算、逻辑运算的函数对象(要求返回值为bool)
▫一元谓词(一个参数):logical_not
▫二元谓词(两个参数):equal_to、not_equal_to、greater、less、greater_equal、less_equal、logical_and、logical_or

•过STL提供的标准函数对象multiplies同样能够实现上面两个实例中对元素实现连乘操作,下面的实例通过调用该标准函数对象来实现相同的功能。

//10_15.cpp#include <iostream>#include <numeric> //包含数值算法头文件#include <functional> //包含标准函数对象头文件using namespace std;int main() {int a[] = { 1, 2, 3, 4, 5 };const int N = sizeof(a) / sizeof(int);cout << "The result by multipling all elements in A is “<< accumulate(a, a + N, 1, multiplies<int>())<< endl; //将标准函数对象传递给通用算法return 0;}

•例10-16:利用STL中的二元谓词函数对象实现了将一个数组按照从大到小的顺序排序的功能

// 10_16.cpp#include <functional>#include<iostream>#include<vector>#include<algorithm>using namespace std;int main() {int intArr[] = { 30, 90, 10, 40, 70, 50, 20, 80 };const int N = sizeof(intArr) / sizeof(int);vector<int> a(intArr, intArr + N);cout << "before sorting:" << endl;copy(a.begin(),a.end(),ostream_iterator<int>(cout,"\t"));cout << endl;sort(a.begin(), a.end(), greater<int>());cout << "after sorting:" << endl;copy(a.begin(),a.end(),ostream_iterator<int>(cout,"\t"));cout << endl;return 0;}

6.2 函数适配器


绑定适配器、组合适配器、函数指针适配器、成员函数适配器


绑定适配器
•binder2nd的实例构造通常比较冗长,bind2nd函数用于辅助构造binder2nd,产生它的一个实例。
•binder1st和bind1st,将一个具体值绑定到二元函数的第一个参数。

•例10-17说明了bind2nd和binder2nd的使用方法。
•例10-17:函数适配器实例——找到数组中第一个大于40的元素

//10_17.cpp#include <functional>#include<iostream>#include<vector>#include<algorithm>using namespace std;int main() {int intArr[] = { 30, 90, 10, 40, 70, 50, 20, 80 };const int N = sizeof(intArr) / sizeof(int);vector<int> a(intArr, intArr + N);vector<int>::iterator p = find_if(a.begin(), a.end(), bind2nd(greater<int>(), 40));if (p == a.end())cout << "no element greater than 40" << endl;elsecout << "first element greater than 40 is: " << *p << endl;return 0;}


组合适配器
•对于一般的逻辑运算,有时可能还需要对结果求一次逻辑反。
•unary_negate和binary_negate实现了这一适配功能。STL还提供了not1和not2辅助生成相应的函数对象实例,分别用于一元谓词和二元谓词的逻辑取反。

•例10-18 ptr_fun、not1和not2产生函数适配器实例。

// 10_18.cpp#include <functional>#include<iostream>#include<vector>#include<algorithm>using namespace std;bool g(int x, int y) {return x > y;}int main() {int intArr[] = { 30, 90, 10, 40, 70, 50, 20, 80 };const int N = sizeof(intArr) / sizeof(int);vector<int> a(intArr, intArr + N);vector<int>::iterator p;p = find_if(a.begin(), a.end(), bind2nd(ptr_fun(g), 40));if (p == a.end())cout << "no element greater than 40" << endl;elsecout << "first element greater than 40 is: " << *p << endl;p = find_if(a.begin(), a.end(), not1(bind2nd(greater<int>(), 15)));if (p == a.end())cout << "no element is not greater than 15" << endl;elsecout << "first element that is not greater than 15 is: " << *p << endl;p = find_if(a.begin(), a.end(), bind2nd(not2(greater<int>()), 15));if (p == a.end())cout << "no element is not greater than 15" << endl;elsecout << "first element that is not greater than 15 is: " << *p << endl;return 0;}

•例10-19 成员函数适配器实例

//10_19.cpp#include <functional>#include <iostream>#include <vector>#include <algorithm>using namespace std;structCar {intid;Car(intid) { this->id = id; }void display() const { cout<< "car " << id << endl; }};intmain() {vector<Car *> pcars;vector<Car> cars;for (int i = 0; i < 5; i++)pcars.push_back(new Car(i));for (int i = 5; i < 10; i++)cars.push_back(Car(i));cout << "elements in pcars: " << endl;for_each(pcars.begin(), pcars.end(), std::mem_fun(&Car::display));cout << endl;cout << "elements in cars: " << endl;for_each(cars.begin(), cars.end(), std::mem_fun_ref(&Car::display));cout << endl;for (size_t i = 0; i < pcars.size(); ++i)delete pcars[i];return 0;}


7、算法

STL算法特点
•STL算法本身是一种函数模版
▫通过迭代器获得输入数据
▫通过函数对象对数据进行处理
▫通过迭代器将结果输出
•STL算法是通用的,独立于具体的数据类型、容器类型

STL算法分类
▫不可变序列算法
▫可变序列算法
▫排序和搜索算法
▫数值算法

不可变序列算法
•不直接修改所操作的容器内容的算法
•用于查找指定元素、比较两个序列是否相等、对元素进行计数等
•例:
template<class InputIterator, class UnaryPredicate>
InputIterator find_if(InputIterator first, InputIterator last, UnaryPredicate pred);
查找[first, last)区间内pred(x)为真的首个元素

可变序列算法
•可以修改它们所操作的容器对象
•包括对序列进行复制、删除、替换、倒序、旋转、交换、分割、去重、填充、洗牌的算法及生成一个序列的算法
•例:
template<class ForwardIterator, class T>
InputIterator find_if(ForwardIterator first, ForwardIterator last, const T& x);
将[first, last)区间内的元素全部改写为x。

排序和搜索算法
▫对序列进行排序
▫对两有序序列进行合并
▫对有序序列进行搜索
▫有序序列的集合操作
▫堆算法
•例:
template <class RandomAccessIterator, class UnaryPredicate>
void sort(RandomAccessIteratorfirst, RandomAccessIteratorlast, UnaryPredicatecomp);
以函数对象comp为“<”,对[first, last)区间内的数据进行排序

数值算法
▫求序列中元素的“和”、部分“和”、相邻元素的“差”或两序列的内积
▫求“和”的“+”、求“差”的“-”以及求内积的“+”和“·”都可由函数对象指定
•例:
template<class InputIterator, class OutputIterator, class BinaryFunction>
OutputIteratorpartial_sum(InputIteratorfirst, InputIteratorlast, OutputIteratorresult, BinaryFunctionop);
对[first, last)内的元素求部分“和”(所谓部分“和”,是一个长度与输入序列相同的序列,其第n项为输入序列前n个元素的“和”),以函数对象op为“+”运算符,结果通过result输出,返回的迭代器指向输出序列最后一个元素的下一个元素

算法应用举例
•例10-20——例10-23演示了几类算法的应用。


本文全部内容都来自教材

 C++语言程序设计(第4版),郑莉,清华大学出版社


0 0