第10章 泛型算法
不同容器都能用的算法
经典算法的公共接口:如排序,搜索
10.1 概述
大多数算法都定义在头文件algorithm中。标准库还在头文件numeric中定义了一组数组泛型算法。
int val = 42; auto res = find(vec.cbegin(), vec.end(), val); cout << "The value " << val << (res == vec.cend() ? "找到" : "未找到") <<endl;
string val = "a value"; auto res = find(lst.cbegin(), lst.cend(), val);
int ia[] = {27,210,12,47,109,83}; int val = 83; int* res = find(begin(ia), end(ia), val); auto res = find(ia+1, ia+4, val);
只需要传入迭代器,但不依赖容器类型;但算法依赖于元素类型的操作
习题10.1
class Solution{ public: int targetNum(vector<int>& v, int target){ int num = count(v.cbegin(), v.cend(), target); return targetNum; } }
10.2 初识泛型算法
10.2.1 只读算法
只会读取其输入范围内的元素,而从不改变元素。
accumulate
int sum = accumulate(vec.cbegin(), vec.cend(), 0); //第三个参数为求和初值
accumulate的第三个参数的类型决定了函数中使用哪个加法运算符以及返回值的类型
//元素拼接 string num =accumulate(v.cbegin(), v.cend(), string("")); string num =accumulate(v.cbegin(), v.cend(), ""); //错误:const char*上没有定义+运算符
equal
判断两个序列是否保存相同的值
接收三个迭代器参数:前两个参数表示第一个序列的范围,第三个参数表示第二个序列的首元素
equal(v1.cbegin(), v1.cend(), v2.cbegin());
基于一个假设:第二个序列至少与第一个序列一样长
小节练习
10.3
int sum = (v.cbegin(), v.cend(), 0);
10.4
存在类型转换,损失精度
10.5
可以比较两个string对象,但比较两个c风格字符串时,实际比较的是指针而非字符串本身。书109页
10.2.2 写容器元素的算法
一些算法将新值赋予序列中的元素
fill
fill(vec.begin(), vec.end(), 0); //将每个元素重置为0 fill(vec.begin(), vec.begin()+vec.size()/2, 10) //一部分置为10
fill_n
接受一个单迭代器,一个计数器,一个值
fill_n(dest, n, val); //迭代器dest开始的n个数 置为val
作用:从迭代器开始的n个数赋值
vector<int> vec; //空vector fill_n(vec.begin(), vec.size(), 0); //所有元素置0
back_inserter
vector<int> vec; auto it = back_inserter(vec); *it = 42; //vec中现在有一个元素,值为42
vector<int> vec; fill_n(back_inserter(vec), 10, 0); //vec中添加10个0
copy
接受三个迭代器,前两个表示输入范围,第三个表示目的序列的起始位置
int a1[] = {0,1,2,3,4,5,6,7,8,9}; int a2[sizeof(a1)/sizeof(*a1)]; //大小一样 //ret指向拷贝到a2的尾元素之后的位置 auto ret = copy(begin(a1), end(a1), a2); //a1赋值给a2
replace
replace(lst.begin(), lst.end(), 0, 42); //lst中所有0替换成42
replace_copy
如果需要原序列保持不变,生成新的序列
replace_copy(lst.cbegin(), lst.cend(), back_inserter(newLst), 0, 42);
lst保持不变,newLst发生为lst的拷贝,不过所有的0改为42
10.2.3 重排容器元素的算法
void elimDups(vector<string>& words){ sort(words.begin(), words.end()); auto end_unqiue = unique(words.begin(), words.end()); //返回迭代器 words.erase(end_unique, words.end()); //传入待删除的起始和终止迭代器 }
排序前:
排序后:
使用unique:
10.3 定制操作
10.3.1 向算法传递函数
一元谓词:接受一个参数
二元谓词:接受两个参数
排序算法:
- sort
bool isShorter(const string& s1, const string& s2){ //二元谓词 return s1.size() < s2.size(); } //按照长度:短->长 sort(words.begin(), words.end(), isShorter);
- stable_sort
//按长度重新排序,长度相同的单词维持字典序 stable_sort(words.begin(), words.end(), isShorter); for(const auto& s : words){ //无拷贝字符串 cout << s << " "; //打印,以空格分割 } cout << endl;
练习10.12
bool compareIsbn(Sales_item data1,Sales_item data2) { return data1.isbn < data2.isbn; } int main() { vector<Sales_item> vec{ Sales_item("623"),Sales_item("524") ,Sales_item("185"),Sales_item("126"), Sales_item("332"),Sales_item("456"),Sales_item("123"),Sales_item("753") }; std::stable_sort(vec.begin(), vec.end(), compareIsbn); for (auto &i : vec) { cout << i.isbn << " "; } system("pause"); return 0; }
10.3.2 lambda表达式
[capture list] (parameter list) -> return type {function body}
- capture list:是一个lambda所在函数中定义的局部变量的列表,通常为空
- return type:返回类型
- parameter:参数列表
- function body:函数体
可以忽略参数列表和返回类型,但必须永远包含捕获列表和函数体
auto f = [] {return 42;} //不接受参数,返回42 cout << f() << endl; //输出42
stable_sort(words.begin(), words.end(), [](const string& a, const string& b) {return a.size() < b.size();} );
[sz] (const string& a){return a.size() >= s2;}; //使用局部变量sz
10.4 再探迭代器
- 插入迭代器:这些迭代器被绑定到一个容器上,可用来向容器插入元素
- 流迭代器:这些迭代器被绑定到输入或输出流上,可用来遍历所关联的IO流
- 反向迭代器:这些迭代器向后而不是向前移动。除了forword_list之外的标准库容器都有反向迭代器
- 移动迭代器:这些专用的迭代器不是拷贝其中的元素,而是移动它们
10.4.1 插入迭代器
插入迭代器有三种:
- back_inserter:创建一个使用push_back的迭代器
- front_inserter:创建一个使用push_front的迭代器
- inserter:创建一个使用insert的迭代器。此函数接受第二个参数,这个参数必须是一个指向给定容器的迭代器。
list<int> lst = {1,2,3,4}; list<int> lst2, lst3; //空lst copy(lst.cbegin(), lst.cend(), front_inserter(lst2)); //lst2={4,3,2,1} copy(lst.cbegin(), lst.cend(), inserter(lst3, lst3.begin())); //lst3={1,2,3,4}
练习10.27
unqiue_copy(vec1.cbegin(), vec2.end(), back_inserter(v2));
10.4.2 iostream迭代器
- istream_iterator:读取输入流
- ostream_iterator:向一个输出流写数据
10.4.3 反向迭代器
- 递增一个反向迭代器(++it)会移动到前一个元素
- 递减一个反向迭代器(–it)会移动到下一个元素
- 除forword_list之外,其他容器都支持反向迭代器
sort(vec.begin(), vec.end()); //正序 sort(vec.rbegin(), vec.rend()); //反序
10.5 泛型算法结构
10.5.1 5类迭代器
10.5.2 算法形参模式
大多数算法具有如下4种形式之一:
alg(beg, end, other args); alg(beg, end, dest, other args); alg(beg, end, beg2, other args); alg(beg, end, beg2, end2, other args);
- alg:算法名字
- dest:表示算法可以写入的目的位置的迭代器
- other args:额外的、非迭代器的特定参数
10.5.3 算法命名规范