文章目录
- 11.2 算法
- 11.2.1 只读算法
- **1.find函数**
- **2.accumulate函数**
- **3.find_first_of 函数**
- 11.2.2 写容器元素算法
- 1.fill函数
- 2.fill_n函数
- 3.back_inserter插入迭代器
- 4.copy函数
- 5.算法的 _copy 版本
- 11.2.3 排序算法
- sort(起始,结束);
- unique(起始,结束)--删除相邻重复元素,返回迭代器(无重复范围的下一位)
- stable_sort(起始,结束)--稳定排序,相等情况下,保持原始顺序
- sort(起始,结束,谓词函数<写函数名即可>)--按照谓词函数规则排序
- 11.3 迭代器
- 11.3.1 插入迭代器
- a. back_inserter--使用push_back实现的插入迭代器
- b.front_inserter--使用push_front实现插入,若容器不支持push_front,不可用
- c.inserter(容器对象,插入起始位置迭代器)总是在该迭代器---前面---位置插入
- 11.3.2 iostream迭代器
- istream_iterator迭代器
- ostream_iterator迭代器
- 在类类型上使用istream_iterator
- 流迭代器限制
- a. 不能从 ostream_iterator 对象读入,也不能写到istream_iterator 对象中
- b. 一旦给 ostream_iterator 对象赋了一个值,写入就提交了。赋值后,没有办法再改变这个值。此外,ostream_iterator 对象中每个不同的值都只能正好输出一次
- c. ostream_iterator 没有 -> 操作符
- 与算法一起使用流迭代器
- 11.3.3 反向迭代器
- 11.3.4 const迭代器
- 11.3.5 五种迭代器
- 11.4泛型算法的结构
- 11.4.1 算法的形参模式
- a. 带有单个目标迭代器的算法
- b. 带第二个输入序列的算法
- 11.4.2 算法命名规范
- a. 区别带有一个值或一个谓词函数参数的算法版本
- b. 区别是否实现复制的算法版本
- 11.5容器特有的算法
标准容器定义的操作比较少,我们需要其他的一些函数来操作容器,比如查找,排序,这些算法不依赖容器类型。
11.2 算法
11.2.1 只读算法
1.find函数
find(起始迭代器,终止迭代器,搜索值)
搜索范围不包含终止迭代器位置,函数返回迭代器类型
#include<iostream>
#include<algorithm>
int main()
{int ia[7] = {52,13,14,100,66,10,66};int search = 66;int *position = std::find(ia,ia+7,search);std::cout << "the value you want is " << search << (position==ia+7 ? " not found" : " found")<< std::endl;std::cout << "ia address is " << ia << " address of found value is " << position << std::endl;
}
find 找到了第一个出现的搜索值,并返回迭代器(指针)
注意:不加 using namespace std; 则需要写 std::find
2.accumulate函数
需要包含头文件,accumulate(起始迭代器,终止迭代器,初始value);
返回范围内的值和初始value的总和;也可以连接字符串;
#include<iostream>
#include<numeric>
int main()
{int ia1[] = {1,2,3,4};int sum = std::accumulate(ia1,ia1+3,100);std::cout << "sum is " << sum << std::endl;
}
string str[] = {"abc","def","ghi","jkl"};string strsum = accumulate(str,str+4,string("ok!!!"));cout << strsum << endl;
3.find_first_of 函数
find_first_of(a.起始迭代器,a.终止迭代器,b.起始迭代器,b.终止迭代器)
返回值:迭代器(指向第一个a中的元素,该元素也在b中存在)
string str[] = {"abc","def","ghi","jkl"};string str1[] = {"jkl","haha","abc"};string *strp = find_first_of(str,str+4,str1,str1+3);cout << *strp << " " << str << " " << strp << endl;
11.2.2 写容器元素算法
1.fill函数
fill(起始迭代器,终止迭代器,填充值)
使得范围内存在的元素进行写入(填充值)
#include<algorithm>
#include<iostream>
using namespace std;
int main()
{int ia[] = {1,2,3,4,5,6};fill(ia,ia+5,9);for(int i = 0; i != 6;++i){cout << ia[i] << " ";}cout << endl;return 0;
}
2.fill_n函数
fill_n(起始迭代器,计数器n,value)
需要保证要写入的元素存在!!!不然,可能导致严重的错误
int ib[] = {1,2,3,4,5,6};fill_n(ib,5,9);for(int i = 0; i != 6;++i){cout << ib[i] << " ";}
3.back_inserter插入迭代器
需要iterator头文件
back_inserter(容器对象)在容器尾部添加元素,返回的是一个迭代器
vector<int> ivec;ivec.push_back(1);ivec.push_back(2);fill_n(back_inserter(ivec),5,3);for(vector<int>::iterator it = ivec.begin();it != ivec.end();++it){cout << *it << " ";}cout << endl;
4.copy函数
copy(a.起始迭代器,a.终止迭代器,b.迭代器)
#include<iostream>
#include<algorithm>
#include<vector>
#include<list>
using namespace std;
int main()
{vector<int> ivec;ivec.push_back(9);list<int> ilist;ilist.push_back(1);ilist.push_back(2);ilist.push_back(3);copy(ilist.begin(),ilist.end(),back_inserter(ivec));for(vector<int>::iterator it = ivec.begin();it != ivec.end();++it){cout << *it << " ";}cout << endl;
}
#include<iostream>
#include<algorithm>
#include<vector>
#include<list>
using namespace std;
int main()
{list<int> ilist;ilist.push_back(1);ilist.push_back(2);ilist.push_back(3);copy(ilist.begin(),ilist.end(),back_inserter(ilist));for(list<int>::iterator it = ilist.begin();it != ilist.end();++it){cout << *it << " ";}cout << endl;
}
以上代码无结果,内存撑爆!
int main()
{vector<int> ivec;ivec.push_back(9);copy(ivec.begin(),ivec.end(),back_inserter(ivec));for(vector<int>::iterator it = ivec.begin();it != ivec.end();++it){cout << *it << " ";}cout << endl;
}
int main()
{vector<int> ivec;ivec.push_back(9);ivec.push_back(9);ivec.push_back(9);list<int> ilist;ilist.push_back(1);ilist.push_back(2);ilist.push_back(3);vector<int>::iterator it = ivec.begin();copy(ilist.begin(),ilist.end(),it+1);for(vector<int>::iterator it = ivec.begin();it != ivec.end();++it){cout << *it << " ";}cout << endl;
}
copy函数没有添加元素个数,只是改写了元素。
5.算法的 _copy 版本
_copy版本不改变输入元素,会创建新序列存储处理的结果
list<int> ilist;ilist.push_back(1);ilist.push_back(2);ilist.push_back(2);ilist.push_back(3);replace(ilist.begin(),ilist.end(),2,6);for(list<int>::iterator it = ilist.begin();it != ilist.end();++it){cout << *it << " ";}cout << endl;
原输入改变了
vector<int> ivec;ivec.push_back(9);list<int> ilist;ilist.push_back(1);ilist.push_back(2);ilist.push_back(2);ilist.push_back(3);replace_copy(ilist.begin(),ilist.end(),back_inserter(ivec),2,6);for(list<int>::iterator it = ilist.begin();it != ilist.end();++it){cout << *it << " ";}cout << endl;for(vector<int>::iterator it = ivec.begin();it != ivec.end();++it){cout << *it << " ";}cout << endl;
原输入list没有改变,把处理后的list追加到vector中了
11.2.3 排序算法
sort(起始,结束);
unique(起始,结束)–删除相邻重复元素,返回迭代器(无重复范围的下一位)
stable_sort(起始,结束)–稳定排序,相等情况下,保持原始顺序
sort(起始,结束,谓词函数<写函数名即可>)–按照谓词函数规则排序
编写程序统计一个文档中长度不小于 4 的单词,并输出输入序列中不重复的单词。
#include<iostream>
#include<vector>
#include<fstream>
#include<algorithm>
#include<string>
using namespace std;
bool isshorter(const string &s1, const string &s2)
{return s1.size() < s2.size();
}
bool gt4(const string &s)
{return s.size() >= 4;
}
string make_end_s(size_t ctr, const string &word, const string &ending)
{return (ctr == 1)?word:word+ending; //多个单词,后缀加s
}
int main(int argc, char **argv)
{if(argc < 2){cerr << "No input file!" << endl;return EXIT_FAILURE;}ifstream infile;infile.open(argv[1]);if(!infile){cerr << "can not open input file!" << endl;return EXIT_FAILURE;}vector<string> words;string word;while(infile >> word)words.push_back(word);sort(words.begin(),words.end());//sort按照字典顺序排序words.erase(unique(words.begin(),words.end()),words.end());//unique指向无重复的末端的下一位,erase删除重复的stable_sort(words.begin(),words.end(),isshorter);//按照isshorter规则,长度从小到大排序,stable排序,长度一样的话不改变之前的字典序vector<string>::size_type wc = count_if(words.begin(),words.end(),gt4);//count_if if判断每个元素是否满足gt4函数,true则wc+1cout << wc << " " << make_end_s(wc,"word","s")<< " 4 characters or longer!" << endl;cout << "unique words:" << endl;for(vector<string>::iterator iter = words.begin();iter != words.end();++iter)cout << *iter << endl;cout << endl;return 0;
}
调试过程如下:
11.3 迭代器
11.3.1 插入迭代器
a. back_inserter–使用push_back实现的插入迭代器
b.front_inserter–使用push_front实现插入,若容器不支持push_front,不可用
在vector或其他没有push_front运算的容器上,不可以使用,将产生错误
c.inserter(容器对象,插入起始位置迭代器)总是在该迭代器—前面—位置插入
#include<list>
#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{list<int> ilist;ilist.push_back(1);ilist.push_back(2);vector<int> ivec;ivec.push_back(6);ivec.push_back(7);list<int>::iterator it = find(ilist.begin(),ilist.end(),2);replace_copy(ivec.begin(),ivec.end(),inserter(ilist,it),6,5);for(it = ilist.begin(); it != ilist.end(); ++it){cout << *it << " ";}cout << endl;return 0;
}
!!!在容器begin首位置inserter,不能跟front_inserter一样产生反序,看下面例子
list<int> ilist,ilist2,ilist3;ilist.push_back(1);ilist.push_back(2);ilist.push_back(3);copy(ilist.begin(),ilist.end(),front_inserter(ilist2));for(list<int>::iterator it = ilist2.begin(); it != ilist2.end(); ++it){cout << *it << " ";}cout << endl;copy(ilist.begin(),ilist.end(),inserter(ilist3,ilist3.begin()));for(list<int>::iterator it = ilist3.begin(); it != ilist3.end(); ++it){cout << *it << " ";}
11.3.2 iostream迭代器
istream_iterator in(strm); | 创建从输入流 strm 中读取 T 类型对象的istream_iterator 对象 |
---|---|
istream_iterator in; | istream_iterator 对象的超出末端迭代器 |
ostream_iterator in(strm); | 创建将 T 类型的对象写到输出流 strm 的ostream_iterator 对象 |
ostream_iterator in(strm, delim); | 创建将 T 类型的对象写到输出流 strm 的ostream_iterator 对象,在写入过程中使用 delim作为元素的分隔符。delim 是以空字符结束的字符数组 |
istream_iterator迭代器
#include<vector>
#include<iterator>
#include<iostream>
#include<fstream>
#include<exception>
using namespace std;
int main(int argc, char** argv)
{vector<int> ivec;ifstream openfile;openfile.open(argv[1]);if(!openfile)throw runtime_error("open file failure!");istream_iterator<int> int_iter(openfile);istream_iterator<int> end;while(int_iter != end){ivec.push_back(*int_iter++);openfile.clear();}for(vector<int>::iterator it = ivec.begin(); it != ivec.end(); ++it){cout << *it << " ";}cout << endl;return 0;
}
以下代码与上面效果相同,用一对迭代器指向的内容初始化vector
istream_iterator<int> int_iter(openfile);istream_iterator<int> end;vector<int> ivec(int_iter,end);
ostream_iterator迭代器
int main(int argc, char** argv)
{ifstream openfile;openfile.open(argv[1]);if(!openfile)throw runtime_error("open file failure!");ostream_iterator<string> out_iter(cout,"\n");istream_iterator<string> int_iter(openfile),end;while(int_iter != end){*out_iter++ = *int_iter++;}return 0;
}
在类类型上使用istream_iterator
提供了>>操作的任何类类型都可以使用istream_iterator
#include<vector>
#include<iterator>
#include<iostream>
#include<fstream>
#include<exception>
#include<Sales_item.h>
using namespace std;
int main(int argc, char** argv)
{ifstream openfile;openfile.open(argv[1]);if(!openfile)throw runtime_error("open file failure!");istream_iterator<Sales_item> item_iter(openfile),end;Sales_item sum;sum = *item_iter++;while(item_iter != end){if(item_iter->same_isbn(sum))sum += *item_iter;else{cout << sum << endl;sum = *item_iter;}++item_iter;}cout << sum << endl;return 0;
}
流迭代器限制
a. 不能从 ostream_iterator 对象读入,也不能写到istream_iterator 对象中
b. 一旦给 ostream_iterator 对象赋了一个值,写入就提交了。赋值后,没有办法再改变这个值。此外,ostream_iterator 对象中每个不同的值都只能正好输出一次
c. ostream_iterator 没有 -> 操作符
与算法一起使用流迭代器
#include<vector>
#include<iterator>
#include<algorithm>
#include<iostream>
using namespace std;
int main()
{istream_iterator<int> cin_iter(cin),end;vector<int> ivec(cin_iter,end);sort(ivec.begin(),ivec.end());ostream_iterator<int> print(cout," ");unique_copy(ivec.begin(),ivec.end(),print);return 0;
}
11.3.3 反向迭代器
反向遍历容器的迭代器
#include<vector>
#include<iostream>
using namespace std;
int main()
{vector<int> ivec;for(vector<int>::size_type i = 0; i!= 10; ++i){ivec.push_back(i);}vector<int>::reverse_iterator r_iter;for(r_iter = ivec.rbegin(); r_iter != ivec.rend(); ++r_iter){cout << *r_iter << " ";}return 0;
}
如果是字符串呢?反向迭代器有什么问题?
#include<cstring>
#include<iterator>
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{string str("first,middle,last"),str1;string::reverse_iterator rcomma = find(str.rbegin(),str.rend(),',');cout << string(str.rbegin(),rcomma) << endl;cout << string(rcomma.base(),str.end()) << endl;return 0;
}
11.3.4 const迭代器
find_first_of(it, roster1.end(),roster2.begin(), roster2.end())
如果该容器是 const 对象,则返回的迭代器是 const_iterator 类型;否则,就是普通的 iterator 类型。
roster1 不是 const 对象,因而 end 返回的只是一个普通的迭代器。
如果将 it 定义为 const_iterator,那么 find_first_of 的调用将无法编译。
because 用来指定范围的两个迭代器的类型不相同。
it 是 const_iterator 类型的对象,而 rotser1.end() 返回的则是一个 iterator 对象。
11.3.5 五种迭代器
Input iterator(输入迭代器) | 读,不能写;只支持自增运算 |
---|---|
Output iterator(输出迭代器) | 写,不能读;只支持自增运算 |
Forward iterator(前向迭代器) | 读和写;只支持自增运算 |
Bidirectional iterator(双向迭代器) | 读和写;支持自增和自减运算 |
Random access iterator(随机访问迭代器) | 读和写;支持完整的迭代器算术运算 |
11.4泛型算法的结构
11.4.1 算法的形参模式
alg (beg, end, other parms); |
---|
alg (beg, end, dest, other parms); |
alg (beg, end, beg2, other parms); |
alg (beg, end, beg2, end2, other parms); |
alg 是算法名称;
beg,end 是算法的输入范围(操作的元素范围);
dest,beg2,end2,都是迭代器;
other parms 是算法的特有其他参数。
a. 带有单个目标迭代器的算法
dest 形参是一个迭代器,用于指定存储输出数据的目标对象。算法假定无论需要写入多少个元素都是安全的。必须确保输出容器有足够大的容量存储输出数据
如果 dest 是容器上的迭代器,则算法将输出内容写到容器中已存在的元素上。
更普遍的用法是,将 dest 与某个插入迭代器(第 11.3.1 节)或者ostream_iterator 绑定在一起。插入迭代器在容器中添加元素,以确保容器有足够的空间存储输出。ostream_iterator 则实现写输出流的功能,无需要考虑所写的元素个数。
b. 带第二个输入序列的算法
算法同时使用 beg2 和 end2 时,这些迭代器用于标记完整的第二个范围。
带有 beg2 而不带 end2 的算法将 beg2 视为第二个输入范围的首元素,但没有指定该范围的最后一个元素。这些算法假定以 beg2 开始的范围至少与 beg和 end 指定的范围一样大。
11.4.2 算法命名规范
a. 区别带有一个值或一个谓词函数参数的算法版本
很多算法通过检查其输入范围内的元素实现其功能。这些算法通常要用到标准关系操作符:== 或 <
sort (beg, end); -----默认升序排序 // use < operator to sort the elements
sort (beg, end, comp); -----用满足comp函数的规则排序// use function named comp to sort theelements
检查指定值的算法默认使用 == 操作符。系统为这类算法提供另外命名的(而非重载的)版本,带有谓词函数形参。带有谓词函数形参的算法,其名字带有后缀 _if:
find(beg, end, val); // find first instance of val in the input range
------find 算法查找一个指定的值
find_if(beg, end, pred); // find first instance for which pred is true
------find_if 算法则用于查找一个使谓词函数 pred 返回非零值True的元素
b. 区别是否实现复制的算法版本
_copy的版本,将元素写到指定的输出目标
reverse(beg, end);
将自己的输入序列中的元素反向重排reverse_copy(beg, end, dest);
复制输入序列的元素,并将它们逆序存储到 dest 开始的序列中。
11.5容器特有的算法
list 容器上的迭代器是双向的,而不是随机访问类型。
由于 list 容器不支持随机访问,因此,在此容器上不能使用需要随机访问迭代器的算法。
这些算法包括 sort 及其相关的算法。
还有一些其他的泛型算法,如 merge、remove、reverse 和 unique,虽然可以用在 list 上,但却付出了性能上的代价。
lst.merge(lst2); lst.merge(lst2, comp) | 将 lst2 的元素合并到 lst 中。这两个 list 容器对象都必须排序。lst2 中的元素将被删除。合并后,lst2 为空。返回 void 类型。第一个版本使用 < 操作符,而第二个版本则使用 comp 指定的比较运算 |
---|---|
lst.remove(val); lst.remove_if(unaryPred) | 调用 lst.erase 删除所有等于指定值或使指定的谓词函数返回非零值的元素。返回 void 类型 |
lst.reverse() | 反向排列 lst 中的元素 |
lst.sort | 对 lst 中的元素排序 |
lst.splice(iter, lst2) | 将 lst2 的元素移到 lst 中迭代器 iter 指向的元素前面。在 lst2 中删除移出的元素;-------第一个版本将 lst2 的所有元素移到 lst 中;合并后,lst2 为空。lst 和 lst2 不能是同一个 list 对象 |
lst.splice(iter, lst2, iter2) | 第二版本只移动 iter2 所指向的元素,这个元素必须是 lst2 中的元素。在这种情况中,lst 和lst2 可以是同一个 list 对象。也就是说,可在一个 list对象中使用 splice 运算移动一个元素。 |
lst.splice(iter, beg, end) | 第三版本移动迭代器 beg 和 end 标记的范围内的元素。beg 和 end 必须指定一个有效的范围。这两个迭代器可标记任意 list 对象内的范围,包括 lst。当它们指定 lst 的一段范围时,如果 iter 也指向这个范围的一个元素,则该运算未定义(iter 应不属于beg,end范围内) |
与对应的泛型算法不同,list 容器特有的操作能添加和删除元素。
list 容器特有的算法与其泛型算法版本之间有两个至关重要的差别。
-
一个差别是 remove 和 unique 的 list 版本修改了其关联的基础容器:真正删除了指定的元素。
例如,list::unique 将 list 中第二个和后续重复的元素删除出该容器。 -
另一个差别是 list 容器提供的 merge 和 splice 运算会破坏它们的实参。
使用 merge 的泛型算法版本时,合并的序列将写入目标迭代器指向的对象,而两输入序列保持不变。
但是,使用 list 容器的 merge 成员函数时,则会破坏它的实参 list 对象------当实参对象的元素合并到调用 merge 函数的list 对象时,实参对象的元素被移出并删除。
#include<iostream>
#include<list>
#include<string>
#include<fstream>
#include<algorithm>
using namespace std;
int main(int argc, char **argv)
{if(argc<2){cerr << "no input file!" << endl;return EXIT_FAILURE;}ifstream infile;infile.open(argv[1]);if(!infile){cerr << "can not open input file!" << endl;return EXIT_FAILURE;}list<string> words;string word;while(infile >> word)words.push_back(word);words.sort();words.unique();cout << "unique words: " << endl;for(list<string>::iterator iter = words.begin();iter != words.end();++iter)cout << *iter << " ";cout << endl;return 0;
}
list.unique(); 直接删除了重复的元素!