泛型算法 - 1【C++ Primer 学习笔记 - 第十一章】

本文探讨了C++ Primer中第十一章的泛型算法,强调算法的类型独立性和通过迭代器操作元素。内容包括:算法如何通过相等操作符(==)进行元素比较,插入迭代器的作用,以及iostream迭代器的使用,如istream_iterator和ostream_iterator,用于读写输入输出流。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

标准容器定义了很少的操作,如添加、删除元素,访问第一个、最后一个元素,获取容器大小等。
但是,用户可能需要其他更多的有用操作,如:排序、查找、查找最大元素、查找最小元素等,
为了应对这种需要,
标准库并没有为每种容器类型都定义实现相应的成员函数,而是定义了一组 泛型算法
因为他们实现共同的操作,因此,称为算法
所谓泛型,指的是,它们可以操作在多种容器类型上,如标准库类型 vector、 list ,内置数组类型、甚至其他类型的序列。

自定义类型,只要与标准库兼容,同样可以使用这些泛型算法。


#include <algorithm>
#include <numeric>


//using std::list;
//using std::find_first_of;
int search_value = 42;
vector<int> ivec;
ivec.push_back(42);
ivec.push_back(10);

vector<int>::const_iterator iter = find(ivec.begin(), ivec.end(), search_value);
cout << "The value " << search_value
	<< (iter == ivec.end() ? " is not present." : " is present.") 
	<< endl;


int ia[6] = {28, 283, 48, 42, 42, 90};	
int *pint = find(ia, ia+6, search_value);
// accumulate:累加求和
// 第三个参数,是累加的初值
// 返回类型,即是第三个实参的类型
// 元素类型必须与第三个实参类型兼容
int isum = accumulate(ia, ia+6, 1);

vector<string> svec;
svec.push_back("a");
svec.push_back("b");

string ssum = accumulate(svec.begin(), svec.end(), string(""));
cout << "The value " << search_value
	<< (pint == ia+6 ? " is not present." : " is present.") 
	<< endl;
cout << "int sum : " << isum << endl;
cout << "string sum : " << ssum << endl;

vector<string> svecUsers;
list<char *> clistAdmins;
svecUsers.push_back("Xiaoming");
svecUsers.push_back("Zhangsan");
svecUsers.push_back("Ligang");

clistAdmins.push_back("Xiaoming");
clistAdmins.push_back("Ligang");
vector<string>::iterator iter2 = svecUsers.begin();
size_t cnt = 0;
// 此处之所以没有将 iter2 定义为 const_iterator
// 是因为:svecUsers.end() 的返回类型,是根据 svecUsers 的类型而定的
// svecUsers 如果是 const 对象,则,返回 const_iterator 类型,
// 否则,返回 iterator 类型。
// 而 find_first_of 算法,要求指定范围的两个迭代器必须具有完全一致的类型,
// svecUsers 不是 const 对象
while((iter2 = find_first_of(iter2, svecUsers.end(), 
       clistAdmins.begin(), clistAdmins.end())) != svecUsers.end())
{
	++cnt;
	++iter2;
}
cout << "Count: " << cnt << endl;
find_first_of , 在第一个范围内查找,返回第一个同时存在于第一、第二范围内的元素。
find、 find_first_of 这类,属于只读算法。

算法固有地独立于类型
大多数情况下,每个算法都需要使用(至少)两个迭代器来指出该算法操纵的元素范围。
find 操作要求元素类型定义了相等(==)操作符,来比较元素。
否则,可使用 find 的重载版本,需要提供一个额外参数:实现元素比较的函数名字

算法从不使用容器操作,因而其实现与类型无关,元素的所有访问、遍历都是通过迭代器实现。
算法使用“普通”迭代器时,有可能会改变元素的值,移动元素,但是,从不修改基础容器的大小
使用了插入器(inserter)这类特殊迭代器的时候,也只会通过插入器来添加元素,
而不会由算法直接添加。

绝大多数算法,都在一段范围内的元素上操作,称为:输入范围(Input Range)
带有输入范围的算法总是使用头两个形参标记该范围。



写容器元素的算法

vector<int> ivec(10,8);
// 前5个,置0
fill(ivec.begin(), ivec.begin() + ivec.size()/2, 0);

//vector<int> ivec2;
//fill_n(ivec2.begin(), 10, 0);
//以上会报错。 
//fill_n 不会检查目标大小是否足够存储要写入的元素

// 用迭代器给元素赋值,被赋值的是迭代器所指向的元素
// 用插入迭代器赋值,会在容器中添加一个新元素

// #include <iterator>
// back_inserter:迭代器适配器
// 与容器适配器一样,迭代器适配器使用一个对象作为实参,
// 并生成一个适应其实参行为的新对象。
// 以下例子,back_inserter 生成一个插入迭代器。
// 试图通过这个迭代器给元素赋值时,
// 将调用 push_back 在容器中添加一个具有指定值的元素
vector<int> ivec2;
fill_n(back_inserter(ivec2), 10, 9);

list<int> ilist(8, 9);
// 以下效率不高,等价于: 
// vector<int> ivec3(ilist.begin(), ilist.end())
vector<int> ivec3;
copy(ilist.begin(), ilist.end(), back_inserter(ivec3));



算法的 _copy 版本


list<int> ilist(10, 5);
replace(ilist.begin(), ilist.end(), 5, 9);

vector<int> ivec;
// replace_copy 会创建副本,替换后,存入 ivec
replace_copy(ilist.begin(), ilist.end(), back_inserter(ivec), 9, 8);

cout << *ilist.begin() << endl; // 9
cout << *ivec.begin() << endl;  // 8


// 谓词 predicate
// 谓词是做某些检测的函数,判断条件是否成立
bool isShorter(const string &s1, const string &s2)
{
	return s1.size() < s2.size();
}
bool GT6(const string &s)
{
	return s.size() >=6;
}

string make_plural(size_t ctr, const string &word, const string &ending)
{
	return (ctr==1) ? word : word + ending;
}


// 以下代码,实现功能:
// 计算输入的单词中,长度 >= 6 的单词个数
// 不包括重复词
vector<string> svec;
string temp_word;

// Ctrl + Z 结束 cin
while(cin >> temp_word)
{
	svec.push_back(temp_word);
}

// sort 进行排序,相同的元素就会被排到相邻的位置
sort(svec.begin(), svec.end());

// unique 算法,“删除”相邻的重复元素,实际上是重新排序
// 返回的迭代器指向,超出无重复的元素范围末端的下一位置
vector<string>::iterator end_unique = unique(svec.begin(), svec.end());

// 调用容器操作,来真正删除元素(算法不直接删除容器元素)
svec.erase(end_unique, svec.end());

// 按照长度排序,相同长度,仍然保持字典顺序
// isShorter 谓词函数,必须接收两个实参,类型与元素类型相同,
// 并返回一个可用于条件检测的值
stable_sort(svec.begin(), svec.end(), isShorter);

vector<string>::size_type wc = count_if(svec.begin(), svec.end(), GT6);
cout << wc << " " << make_plural(wc, "word", "s") << " 6 characters or longer" << endl;


C++ 提供的另外3种迭代器:插入迭代器(insert iterator)、iostream迭代器(iostream iterator)、反向迭代器(reverse iterator)
在 iterator 头文件中定义

插入器,是一种迭代器适配器。带有一个容器参数,并生成一个迭代器,用于在指定容器中插入元素。
三种插入器:back_inserter、front_inserter、inserter。

front_insert 需要使用 push_front ,因此容器需要提供 push_front 操作,像 vector 这类不支持 push_front 操作的容器,则无法使用 front_inserter

list<int> ilist;
ilist.push_back(20);
ilist.push_back(1);

vector<int> ivec;
ivec.push_back(10);
ivec.push_back(5);

list<int>::iterator iter = find(ilist.begin(), ilist.end(), 20);
// 将 ivec 中的 10 替换成 8,
// 创建相应副本,插入到 ilist 的 iter 指向的位置前面
replace_copy(ivec.begin(), ivec.end(),
	inserter(ilist, iter), 10, 8);


list<int> ilist, ilist2, ilist3, ilist4;
// ilist: 3, 2, 1, 0
for(list<int>::size_type i=0; i!=4; ++i)
{
	ilist.push_front(i);
}

// ilist2: 0, 1, 2, 3
copy(ilist.begin(), ilist.end(), front_inserter(ilist2));

// ilist3: 3, 2, 1, 0
// 元素在ilist3 的固定位置插入
// 该位置,插入第一个元素之前,是头部,
// 插入一个元素之后,就不是头部了。
copy(ilist.begin(), ilist.end(), inserter(ilist3, ilist3.begin()));

// ilist4: 3, 2, 1, 0
copy(ilist.begin(), ilist.end(), back_inserter(ilist4));
list<int>::iterator iter = ilist4.begin();



iostream 迭代器

iostream 类型不是容器,但标准库同样提供了在 iostream 对象上使用的迭代器
istream_iterator 用于读取输入流, ostream_iterator 用于写输出流

将对应的流,视为特定类型的元素序列。
流迭代器只定义了基本的迭代操作:自增、解引用、赋值。
istream 迭代器可以比较是否相等;
ostream 迭代器不提供比较运算

istream_iterator<T> in(strm);

创建从输入流 strm 中读取 T 类型对象的 istream_iterator 对象

istream_iterator<T> in;
istream_iterator 对象的超出末端迭代器

ostream_iterator<T> in(strm);
创建将 T 类型的对象写到输出流 strm 的 ostream_iterator 对象

ostream_iterator<T> in(strm, delim);
创建将 T 类型的对象写到输出流 strm 的 ostream_iterator 对象,
在写入过程中使用 delim 作为元素的分隔符。delim 是以空字符结束的字符数组

istream_iterator 的操作
it1 == it2
 
比较两个 istream_iterator 对象是否相等。迭代器读取的必须是相同的类型

it1 != it2
如果两个迭代器都是 end 值,则相等。
对于两个都不指向流结束位置的迭代器,如果它们使用同一个流输入构造,则它们也相等

*it
返回从流中读取的值

it->mem
相当于:(*it).mem。 返回从流中读取的对象的 mem 成员

++it
it++

通过使用元素类型提供的 >> 操作符,从输入流中读取下一个元素的值,使迭代器向前移动。
通常,
前缀版本,迭代器在流中向前移动,再返回加1 后的迭代器的引用。
后缀版本,迭代器在流中向前移动,再返回迭代器的原值。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值