【C++ Primer】第10章 泛型算法

简介: 【C++ Primer】第10章 泛型算法

第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 插入迭代器



插入迭代器有三种:


  1. back_inserter:创建一个使用push_back的迭代器


  1. front_inserter:创建一个使用push_front的迭代器


  1. 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 算法命名规范



目录
相关文章
|
10天前
|
存储 算法 C++
C++提高篇:泛型编程和STL技术详解,探讨C++更深层的使用
文章详细探讨了C++中的泛型编程与STL技术,重点讲解了如何使用模板来创建通用的函数和类,以及模板在提高代码复用性和灵活性方面的作用。
27 2
C++提高篇:泛型编程和STL技术详解,探讨C++更深层的使用
|
6天前
|
存储 算法 安全
超级好用的C++实用库之sha256算法
超级好用的C++实用库之sha256算法
12 1
|
6天前
|
存储 算法 安全
超级好用的C++实用库之国密sm4算法
超级好用的C++实用库之国密sm4算法
16 0
|
6天前
|
算法 安全 Serverless
超级好用的C++实用库之国密sm3算法
超级好用的C++实用库之国密sm3算法
12 0
|
6天前
|
算法 数据安全/隐私保护 C++
超级好用的C++实用库之MD5信息摘要算法
超级好用的C++实用库之MD5信息摘要算法
13 0
|
2月前
|
算法 C++ 容器
C++标准库中copy算法的使用
C++标准库中copy算法的使用
20 1
|
2月前
|
算法 搜索推荐 C++
c++常见算法
C++中几种常见算法的示例代码,包括查找数组中的最大值、数组倒置以及冒泡排序算法。
18 0
|
2月前
|
算法 C++ 容器
【C++算法】双指针
【C++算法】双指针
|
3月前
|
安全 编译器 C++
C++一分钟之-泛型Lambda表达式
【7月更文挑战第16天】C++14引入泛型lambda,允许lambda接受任意类型参数,如`[](auto a, auto b) { return a + b; }`。但这也带来类型推导失败、隐式转换和模板参数推导等问题。要避免这些问题,可以明确类型约束、限制隐式转换或显式指定模板参数。示例中,`safeAdd` lambda使用`static_assert`确保只对算术类型执行,展示了一种安全使用泛型lambda的方法。
43 1
|
3月前
|
机器学习/深度学习 算法 搜索推荐
下一篇
无影云桌面