C++迭代器与泛型算法(下)
迭代器再探
插入迭代器
是一种迭代器适配器。接受一个容器,生成一个迭代器,能实现向给定容器添加元素。
-
back_inserter:使用push_back的迭代器,正向
-
front_inserter:使用push_front的迭代器,头插,反向
-
inserter:使用insert的迭代器,正向
inserter迭代器的返回值仍为指向其本身的迭代器,与下面的代码相同
inserter(it,it.begin()); //添加元素后it仍指向原来的元素 //等价于: auto it=a.insert(it,val); //此时it指向新添加的元素 ++it; //让it重新指向之前的元素 使用copy算法复制容器
deque<int> a{ 1,2,3,4,5,6 }; list<int> b, c; copy(a.cbegin(), a.cend(), front_inserter(b)); copy(b.cbegin(), b.cend(), inserter(c, c.begin())); 使用unique_copy将不重复的元素拷贝到list:
vector<int> a{ 1,1,2,2,3,4,5,6,7,7,8,8,9,10 }; list<int> b; sort(a.begin(), a.end()); unique_copy(a.cbegin(), a.cend(), inserter(b,b.begin())); 各容器适用范围:
vector:back_inserter inserter
deque:全部适用
list:全部适用
forward_list:只适用front_inserter
流迭代器
istream_iterator操作
istream_iterator指定迭代器将要读的内容,因此要读取的类型必须定义了输入运算符>>。
istream_iterator<int> int_int(cin); //从cin读取int istream_iterator<int> int_eof; //尾后迭代器 ifstream in("文件名.txt"); istream_iterator<string> str_in(in); //从文件读取string
PS:一个空的istream_iterator迭代器即是一个eof标记,可用作输入结束标记
从cin读取元素:
vector<int> a; istream_iterator<int> int_int(cin); //从cin读取int istream_iterator<int> eof_hhh; //尾后迭代器,用做输入结束 while (int_int != eof_hhh) { //istream_iterator读取数据 到vector 容器 a.push_back(*int_int++); }
改写:一个比较牛逼的写法
istream_iterator<int> int_int(cin),eof_hhh; vector<int> a(int_int, eof_hhh); //从迭代器范围来构建对象,从cin读取,直到遇见eof
算法操作迭代器
istream_iterator<int> int_int(cin),eof_hhh; cout << accumulate(int_int, eof_hhh, 0) << endl; //读取输入,自动求和
ostream_iterator操作
ostream_iterator指定迭代器将要输出的内容,因此要输出的类型必须定义了输出运算符<<。
可以指定第二个参数:一个字符串字面常量或者字符数组的指针,表示在输出每个元素后都会打印此字符串。
不允许空的ostream_iterator或表示尾后的ostream_iterator。
将元素写到cout:
ostream_iterator<int> out_iter(cout, " "); vector<int> a{ 1,2,3,4,5,6,7,8,9 }; for (auto e : a) { *out_iter++ = e; //实际上将e元素写到了cout } *out_iter = '\n';
在向ostream_iterator写入时,可以忽略 * 和 ++运算符,没有任何影响,但是不应该省略,为了清晰和理解。
比循环更简单的写法:
ostream_iterator<int> out_iter(cout, " "); vector<int> a{ 1,2,3,4,5,6,7,8,9 }; copy(a.cbegin(), a.cend(), out_iter); //调用copy来使vector的每一个元素都写入到cout里去
示例
- 从一个文件读取string,写到vector< string >,并输出vector
ifstream out_file("1.txt"); //打开文件 istream_iterator<string> string_in(out_file),eof; //定义输入和超尾迭代器 ostream_iterator<string> out(cout, "\t"); //打印 vector<string> a{ string_in,eof }; //根据迭代器构造容器 copy(a.cbegin(), a.cend(), out); //打印容器
- copy与输入输出的妙用:
istream_iterator<int> in(cin), eof; ostream_iterator<int> out(cout, " "); vector<int> a; copy(in, eof, inserter(a, a.begin())); //此copy可以给容器赋值 sort(a.begin(), a.end()); copy(a.cbegin(), a.cend(), out); //此copy可以打印a的值
- 从文件读取整数,偶数放在一个文件,奇数放在一个文件,一个比较漂亮的写法:
ifstream in_file("输入.txt"); ofstream out_file1("输出1.txt"),out_file2("输出2.txt"); istream_iterator<int> in(in_file), eof; vector<int> temp{ in,eof }; ostream_iterator<int> out1(out_file1, "\n"), out2(out_file2, " "); //偶数在前,奇数在后 auto iter=partition(temp.begin(),temp.end(), [](const int& num) {return num % 2;); copy(temp.begin(), iter,out1); //偶数 copy(iter, temp.end(), out2); //奇数
反向迭代器
就是在容器中从尾元素到首元素反向移动的迭代器。同时 ++ --等运算符的顺序也会相反。
除了forward_list之外,其他的容器都支持反向迭代器。
指向一个尾元素和首元素的前一个元素。反向迭代器也有const和非const版本。
逆序打印容器中的元素:
list<int> a{ 1,2,3,4,5,7,8,9 }; for (auto r_iter = a.crbegin(); r_iter != a.crend(); ++r_iter) { //注意:反向变了,++也会执行相反的操作,++会往前移动。 cout << *r_iter << " "; }
sort逆序排序容器元素:
sort(a.rbegin(), a.rend());
forward_list和流迭代器都不支持创建反向迭代器。
找到一个结束字符后,打印之前的内容:
string temp{"woainiylh,wdada"}; auto iter = find(temp.cbegin(), temp.cend(), ','); cout << string(temp.cbegin(), iter) << endl; //正向打印 ‘,’之前的内容
如果我们采用反向打印:
auto riter = find(temp.crbegin(), temp.crend(), ','); cout << string(temp.crbegin(), riter) << endl; //反向打印 ‘,’之前的内容
我们很容易想到,结果一定使错误的,因为他会从尾部反向打印,元素顺序也会发生变化。
因此我们应该采用反向转正向的方式: base函数
auto riter = find(temp.crbegin(), temp.crend(), ','); cout << string(riter.base(),temp.cend()); //base函数将反向迭代器转化为一个普通迭代器(正向)
注意迭代器的特性:左闭右开。
示例:
-
使用reverse_iterator逆序打印一个vector:
copy(a.crbegin(), a.crend(), out); -
将一个有10个元素的vector的第3到第7个元素逆序拷贝到list中:
vector<int> a{ 1,2,3,4,5,6,7,8,9,10 }; list<int> b{ a.crbegin() + 3,a.crend() - 2 }; //第一种方法 /*auto it1 = find(a.cbegin(), a.cend(), 3); //第二种方法 auto it2 = find(a.cbegin(), a.cend(), 7); copy(it1, it2+1, front_inserter(b));*/ for_each(b.cbegin(), b.cend(), [](const int& num) {cout << num << " "; });
泛型算法结构
五类迭代器类型
-
输入迭代器:只读,只能递增
关系运算符,递增运算符,解引用运算(只出现在赋值运算符的右侧),箭头运算符
单遍扫描算法:find accumulate 算法;istream_iterator就是一个输入迭代器。
-
输出迭代器:只写,只能递增
递增运算符,解引用运算(只出现在赋值运算符的左侧)
单遍扫描算法:copy算法;ostream_iterator就是一个输出迭代器。
-
前向迭代器:可读写,只能递增
支持输入输出的所有操作
多遍扫描:replace算法;forward_list上的迭代器是前向迭代器。
-
双向迭代器:可读写,可以递增递减
支持输入输出和前向的所有操作
多遍扫描:reverse算法;除了forward_list之外,标准库都提供符合双向迭代器要求的迭代器。
-
随机访问迭代器:可读写,支持全部迭代器运算
支持输入输出前向和双向的所有操作
多遍扫描:sort算法;array deque string vector的迭代器都是随机访问迭代器。
算法形参模式
- arg(beg,end,other):一个范围,一个其他操作
- arg(beg,end,dest,other):一个范围,一个目标,一个其他操作
- arg(beg1,end1,beg2,other):一个范围,一个目标起点,一个其他操作
- arg(beg1,end1,beg2,end2,other):一个范围,一个目标范围,一个其他操作
算法的命名
同一算法可以重载为接受谓词:
unique(beg,end); 使用默认==
unique(beg,end,comp); 使用定义的comp
_if版本的算法:
同样是一个接受谓词的版本:
find(beg,end,val) 查找val第一次出现
fin_if(beg,end,pred) 查找pred函数为真的位置
拷贝元素的版本: 将重排后的序列拷贝到新的序列
reverse 和 reverse_copy
remove_if 和 remove_copy_if
特定容器的算法
list(双向迭代器)和forward_list(前向迭代器)由于不支持随机访问,因此他们有自定义的成员函数:
sort ,merge , remove ,reverse , unique
1:sort()需要随机访问迭代器,所以不能用于list和forward_list.
2:对于list和forward_list应优先使用其成员函数版本的算法,皆返回void.
3:remove()删除元素,reverse()反转元素顺序,sort()排序,unique()删除相同元素
4:链表类型还定义了splice成员算法,其实链表数据结构所特有的,主要用于合并两个链表
使用list实现重复单词去重:
list<string> a; string temp; while (cin >> temp) { a.push_back(temp); } a.sort(); a.unique(); for_each(a.cbegin(), a.cend(), [](const string& s) {cout << s << " "; });
本文来自博客园,作者:hugeYlh,转载请注明原文链接:https://www.cnblogs.com/helloylh/p/17209736.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!