IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> C++泛型算法 《C++Primer》第10章 -> 正文阅读

[数据结构与算法]C++泛型算法 《C++Primer》第10章

1、泛型算法思想

  • 一个算法可用于多种数据类型,算法与所操作的数据结构分离
  • 泛型使用迭代器作为 算法 与 数据结构 的桥梁

1.1 引 例:find函数

find函数能找到序列中目标值的位置,他接受三个参数,分别是序列的起止位置,以及要找的值。

为了充分理解泛型算法【思想】,我们来看find函数如何工作:

  • 1:访问序列中的首元素
  • 2:比较此元素与我们要查找的值
  • 3:如果此元素与我们要查找的值匹配,返回【标识】此元素的值。
  • 4:否则,前进到下一个元素,重复步骤2和3。
  • 5:如果到达序列尾,算法停止。
  • 6:如果到达序列末尾,应该返回一个能表示未找到的值。此值和步骤3返回的值必须具有相容的类型。

可以看到,上述步骤并没有对查找的序列做任何限制,所以传入的既可以是个容器,也可以是个数组等。

1.2 借back_inserter进一步理解“标准库算法不改变容器大小”

前面学习到,标准库算法不会改变他们所操作的容器的大小,但杀出了back_inserter这么个东西,使得标准库能够向容器中添加新元素。这不是矛盾了吗???

严格来说,标准库算法根本不知道有【容器】这个东西。它们只接受迭代器参数,通过迭代器访问元素。

因此,当传递给算法普通迭代器时,这些迭代器只能访问元素,造成的效果就是算法只能读取、移动、改变元素值,但无法添加或删除元素。

但当我们传递给算法插入器,例如back_inserter时,由于这类迭代器能调用下层容器的操作来向容器插入元素,造成的算法执行的效果就是向容器中添加了元素。

因此,关键要理解:标准库算法【从来不直接操作容器】,它们【只操作迭代器】,从而【间接访问】容器。能不能插入和删除元素,【不在于算法,而在于传递给它们的迭代器是否具有这样的能力】

在这里插入图片描述

算法是不应该知道容器的存在的!!!

算法你只管机械化的算,完成任务,不用知道对象是谁。
算法:“只要我有迭代器,我就能完成任务,容器??容器是啥???有啥用?”

2、lambda表达式

2.1 lambda的作用

lambda表达式可以让我们把函数当作【数据】一样传递,使我们从繁琐的语法中解放出来,更加关注于 “算法” 本身。
lambda表达式完全可以取代谓词。

2.2 使用lambda表达式有如下几点限制:

  • 捕获列表只用于局部非静态变量。
  • lambda可以直接使用定义在当前函数之外的名字,所以可以直接使用cout

2.3 捕获列表:

[]不捕获任何变量,这种情况下lambda表达式内部不能访问外部的变量。
[&]以引用方式捕获所有变量
[=] 用值的方式捕获所有变量(可能被编译器优化为const &
[=, &foo]以引用捕获变量foo, 但其余变量都靠值捕获
[&, foo] 以值捕获foo, 但其余变量都靠引用捕获
[bar]以值方式捕获bar; 不捕获其它变量
[this]捕获所在类的this指针 (Qt中使用很多,如此lambda可以通过this访问界面控件的数据)

如果想改变以值的方式捕获的变量的话,要用mutable关键字,如下图所示:

int x = 1;
auto func = [x] () mutable { return ++x; };    此时,lambda的参数列表的一对括号()就不能省略了
cout << func() << endl;

3、bind

3.1 bind重排参数的妙用

按单词长度【由短至长】排序
sort(words.begin(), words.end(), isShorter);
按单词长度【由长至短】排序
sort(words.begin(). words.end(), bind(isShorter, _2, _1));

3.2 bind函数需注意:非占位符参数是直接拷贝的

所以,当必须传引用时(如cout),需要用ref来传。如下面的例子所示:

ostream& print(ostream& os, const string& s);
for_each(s.begin(), s.end(), bind(print, cout, _1);				错误,不能拷贝一个ostream
for_each(s.begin(), s.end(), bind(print, ref(cout), _1);		正确

bind里要想传递一个对象而又不拷贝他,只能使用标准库函数ref

目前bind有什么用呢?

答:为替代lambda提供一个办法。当lambda不捕获局部变量时,用函数替代它很容易,但当lambda捕获局部变量时就不行了,这时,bind就能做到了。
在这里插入图片描述

bind接受几个参数???

bind是可变参数的。它接受的第一个参数是一个【可调用对象】,即实际工作函数A,返回【供算法使用】的【新的可调用对象B】。若A接受x个参数,则bind的参数个数应该是x+1,即除了A外,其他参数应【一一对应】A所接受的参数。这些参数中有【一部分来自于B(_n)】,另外一些来自于【所处函数的局部变量】。

不要把调用bind所返回的可调用对象 和 bind搞混了啊!Kora-a–a—a!!!
find函数返回的可调用对象就是一个函数适配器,它是通过底层存在的原函数修改参数(修改、增加等)变成的新的函数。

4、再探迭代器

4.1 插入迭代器

back_inserterfront_inserterinserter

使用方法:使用这三个成员返回的迭代器【解引用直接赋值】,即可完成在相应位置插入元素。

list<int> v{ 1, 2, 3, 4, 5, 6, 7, 8, 9 };
auto beg = v.begin();

auto iter = inserter(v, beg);
*iter = 100000;

auto fiter = front_inserter(v);
*fiter = 2133214;

auto biter = back_inserter(v);
*biter = 0;
for (auto i : v) cout << i << ' ';

注意:

  • 赋完值完成一次插入后可以接着用,仍然是在【原来的位置】前面插入。
  • 这些迭代器【只能】用于直接赋值插入元素,连解引用访问元素都不行。

4.2 iostream迭代器

istream_iterator操作:

在这里插入图片描述

注意:

  • T具有>>运算符时,才能创建istream_iterator对象。

istream_iterator从标准输入读取int类型,存入vector

istream_iterator<int> is_iter(cin);
istream_iterator<int> eof;					尾后迭代器
while (is_iter != eof)
	vec.push_back( *is_iter++ );	

循环里的代码:【先返回】迭代器的【旧值】,然后【解引用迭代器】,获得从流中读取的前一个值


ostream_iterator操作:

在这里插入图片描述

注意:

  • T具有<<运算符时,才能创建ostream_iterator对象。
  • ostream_iterator必须绑定到一个流,不允许空的或表示尾后位置的ostream_iterator

ostream_iterator输出序列:

vector<int> v{ 1, 2, 3, 4, 5, 6, 7, 8, 9 };
ostream_iterator<int> out_iter(cout, " ");
for (auto i : v)  *out_iter++ = i;				这里out_iter加不加*++都行,但这样写更加清晰

每次向ous_iter赋值,写操作就会被提交。
上面的代码也可以写成如下这种形式,更简单一些:

copy(vec.begin(), vec.end(), out_iter);			把`vec`【复制到输出流】中,也就是输出 出去了

4.3 反向迭代器

forward_list和流迭代器没有反向迭代器。

reverse_iterator::base()返回调用此成员的反向迭代器的普通迭代器。

vector<int>::reverse_iterator riter(iter)这样可以把正向迭代器强转成反向迭代器。

注:反向迭代器与普通迭代器的转换是闭合区间的转换,他们左右的开闭是反着的。

4.4 迭代器类型

  • 输入迭代器:只读,不写,单遍扫描,只能递增,支持==!=,解引用运算符*(且只能出现在赋值运算符右侧,因为只能读)和箭头运算符->
  • 输出迭代器:只写,不读,单遍扫描,只能递增,支持解引用运算符*(且只出现在赋值运算符左侧,因为只能写)。
  • 前向迭代器:可读写,多遍扫描,只能递增,支持所有输入、输出迭代器的操作。
  • 双向迭代器:可读写,多遍扫描,可递增递减,支持所有前向迭代器操作。
  • 随机访问迭代器:全能,支持【所有】操作。(比上面的多了:> < >= <= + - += -= ,以及两个迭代器相减)

4.5 链表容器【特有的算法】

在这里插入图片描述在这里插入图片描述
splice成员,这些成员进行链表插入链表的操作:
在这里插入图片描述
由于并非通过迭代器实现,所以这些操作基本都会修改链表大小。

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-03-10 22:50:47  更:2022-03-10 22:52:11 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 13:54:15-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码