10.2初始泛型算法

初识泛型算法

标准库提供了100多个算法,幸运的是这些算法都有一致的结构。

除了少数例外外,这些算法都对一个范围内的元素进行操作,我们将此元素范围称为输入范围,接受输入范围的算法总是使用前两个参数来表示此范围。

虽然大多数算法遍历输入范围的方式相似,但使用范围中的元素的方式不同。

1.只读算法

①find函数:

  • find函数作用是在一个未排序的元素序列中查找给定值是否在所给范围内。
  • find函数参数:find(iter1, iter2, val)。其中iter1和iter2表示一个元素范围的迭代器,第三个参数是一个值。
  • find函数的返回值:如果输入范围内有匹配的元素,则返回指向第一个等于给定值的元素迭代器。如果无匹配元素,则返回第二个参数来表示搜索失败。可以通过比较返回值和第二个参数判断是否搜索成功

由于find操作的是迭代器,因此可以用同样的find函数在任何容器中查找值。

如:在string的list中查找一个给定值

string val = "a value";
list<string> lst{"abc", "bcd", "a value", "akz"};
auto res = find(lst.cbegin(), lst.cend(), val);
cout << "The value " << val 
     << (res == lst.cend() ? " is not present" : " is present") 
     << endl;
//结果为:The value is present

②count函数

  • count函数的作用是统计容器中等于value元素的个数。
  • count函数参数:count(iter1,iter2,value)。 其中iter1和iter2表示一个元素范围的迭代器,第三个参数是一个值。
  • count函数的返回值:如果输入范围内有匹配的元素,则返回匹配元素的个数;如果输入范围内没有匹配的元素,则返回0。

如:在string中查找一个字符出现的次数

string val = "a value";
int res = count(val.begin(), val.end(), 'a');
cout << res << endl;
//结果为:2

③accumulate函数

  • accumulate函数的作用是对输入范围内的值进行求和。
  • accumu函数的参数:accumulate(iter1,iter2,val)。其中iter1和iter2表示一个元素范围的迭代器,第三个参数是求和起点。

注意:第三个参数的类型决定了函数中使用哪个加法运算符以及返回值的类型。将第三个参数作为求和起点,则要求将元素加到和的类型上的操作是可行的,即,序列中元素的类型必须与第三个参数匹配,或者能转换为第三个参数的类型。

  • accumulate的返回值:与第三个参数的类型一致。

如下两个例子:

//例1:对vec中元素求和,和的初值是1
vector<int> vec{1, 2, 3};
int res = accumulate(vec.begin(), vec.end(), 1); //1+1+2+3
cout << res << endl;
//输出结果为:7    

//例2:链接字符串
list<string> lst{"abc", "def", "hij"};
string sum1 = accumulate(lst.begin(), --lst.end(), string());
string sum2 = accumulate(lst.begin(), --lst.end(), "");
cout << sum1 << endl; //正确:string类上定义了+运算符,结果为:abcdef
cout << sum2 << endl; //错误:""是const char*类型,没有定义+运算符

④equal(操作两个序列的算法)

  • equal函数的作用是用于确定两个序列是否保存相同的值。
  • equal函数的参数:equal(iter1,iter2,iter3)。前两个迭代器表示第一个序列的元素范围,第三个迭代器表示第二个序列的首元素

注意:接受单一迭代器表示第二个序列的算法都假定第二个序列至少与第一个序列一样长。第二个序列比第一个序列长的话就比较第二个序列与第一个序列一样长的部分。

  • equal函数返回值:如果两个序列所有元素都相等返回true;否则返回false。
vector<int> v1{1, 2, 3};
vector<int> v2{1, 2, 3, 4};
bool res = equal(v1.begin(), v1.end(), v2.begin());
cout << res << endl;
//输出结果为1

2.写容器元素的算法

一些算法将新值赋予序列中的元素。当我们使用写算法时,必须确保序列原大小至少不小于我们要求算法写入的元素数目算法不会执行容器操作(只有容器操作才会改变容器的大小),因此这些算法不可能改变容器的大小。

一些算法会自己向输入范围写入元素。这些算法并不危险,因为它们最多写入与给定序列一样多的元素。

一些算法接受一个迭代器来指出一个单独的目的位置。这些算法将新值赋予一个序列中的元素,该序列从目的位置迭代器指向的元素开始

①fill函数

  • fill函数的作用是将给定的值赋予输入序列中的每个元素。
  • fill函数的参数:**fill(iter1,iter2,val)。**前两个迭代器表示一个元素范围的迭代器,第三个参数表示要写入的值。
  • fill函数的返回值:void

如:将元素范围内全写入0

vector<int> v1{1, 2, 3};
fill(v1.begin(), v1.end(), 0);

②fill_n函数

  • fill_n函数的作用是将给定的值目的位置写入指定个元素
  • fill_n函数的参数:fill_n(dest,n,val),其中dest是目的位置,n是指定的元素个数,val是给定的值。

注意:向目的位置迭代器写入数据的算法假定目的位置足够大,能偶容纳要写入的元素,否则,将产生严重的错误。

  • fill_n函数的返回值:void

如:在vector容器给定位置插入3个0

vector<int> v{1, 2, 3, 4, 5, 6};
fill_n(v.begin(), 3, 0); //v的值为{0, 0, 0, 4, 5, 6}
fill_n(v.begin() + 4, 3, 0); //错误:目的位置容纳不了要写入的元素

如果我们想保证算法有足够的空间来容纳输出数据,可以使用插入迭代器back_inserter。

back_inserter接受一个指向容器的引用返回一个与容器绑定的插入迭代器。通过此迭代器赋值时,赋值运算符会调用push_back函数插入,我们常常使用back_inserter来创建一个迭代器,作为算法的目的位置来使用。

如:在一个空的vector容器中插入10个0

vector<int> vec;
fill_n(back_inserter(vec), 10, 0); //通过调用push_back给vec添加元素

③拷贝算法copy函数

  • copy函数的作用是将一个输入范围的元素拷贝到目的序列的起始位置,传递给copy的目的序列至少要包含与输入序列一样多的元素
  • copy函数的参数:**copy(iter1,iter2,dest),**iter1和iter2组成输入范围的元素,dest是目的位置的迭代器,将输入范围的元素拷贝到目的位置。
  • copy函数的返回值:返回目的位置迭代器(递增后)的值,即拷贝到目的位置最后一个元素之后的位置。

如:将数组a1中元素拷贝到数组a2中

int a1[] = {0, 1, 2, 3, 4, 5};
int a2[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int *res = copy(begin(a1), end(a1), begin(a2));
cout << *res << endl; //结果为7,迭代器返回拷贝之后下一个位置

④拷贝算法replace函数

  • repalce函数的作用是读入一个序列,并将其中所有等于给定值的元素都改为另一个值。
  • replace函数的参数:**replace(iter1,iter2,val1,val2).**iter1和iter2组成一个输入范围,val1表示要搜索的值val2表示要替换的值
  • replace函数的返回值:void

如:将string中所有等于小写字母a的替换成大写字母A。

string s{"a value"};
replace(s.begin(), s.end(), 'a', 'A');
cout << s << endl; //输出结果为: A vAlue

⑤拷贝算法replace_copy

  • replace_copy函数的作用是保留原序列不变,将原序列中等于给定值的元素都改为另一个值,并保存到同一个新序列中。
  • repace_copy函数的参数:**replace_copy(iter1,iter2,dst,val1,val2)。**其中iter1和iter2组成一个输入范围,**dst是一个迭代器,指出调整后序列保存的位置。**val1表示要搜索的值,val2表示要替换的值。

如:将string中所有等于小写字母a的替换成大写字母A,并分别保存到两个string对象s1和s2中

string s{ "a value" };
string s2{"oh le my god"};
string s3; //空string,可以通过插入迭代器插入
replace_copy(s.begin(), s.end(), s2.begin(),'a', 'A');
replace_copy(s.begin(), s.end(), back_inserter(s3), 'a', 'A');
cout << s2 << endl << s3 << endl;
//输出结果为:
 //A vAluey god
 //A vAlue

3.重排容器元素的算法

某些算法会重排容器中元素的顺序,如sort。调用sort会重排输入序列中的元素,它是利用元素类型的<运算符来实现排序的。

①sort函数

  • sort函数的作用是按照元素的<运算符重排输入序列中的元素。
  • sort函数的参数:sort(iter1,iter2).iter1和iter2组成一个输入范围,sort函数利用元素类型运算符<将输入范围内的元素重新排序(升序)
  • sort函数的返回值类型:void

如:将下面的包含string的vector容器重新排序

vector<string> vec{ "fox", "jumps", "over", "the", "slow", " red", "turtle" }; //狐狸跳过缓慢的红龟
sort(vec.begin(), vec.end());
for (string s : vec) {
	cout << s << " ";
}
//结果为:fox jumps over quick red slow the turtle

②unique函数

  • unique函数的作用是将相邻的重复项“消除”,并返回一个指向不重复值范围末尾的迭代器。

注意:unique并不真的删除任何元素,只是覆盖相邻的重复元素,使得不重复元素出现在序列开始部分。unique返回的迭代器指向最后一个不重复元素之后的位置。此位置之后的元素仍然存在,但我们不知道它们的值是多少。

  • unique函数的参数:unique(iter1,iter2),iter1和iter2组成一个输入范围,消除输入范围内相邻的重复元素。
  • unique函数的返回类型:指向容器元素的迭代器。

如:给定一个字符串,消除其中相邻的字母

string s{ "aaahuanggya" }; 
auto it = unique(s.begin(), s.end());
cout << s << endl;
cout << *it << endl;
//结果为:
 //ahuangyagya
 //g

4.接收谓词的函数

①sort函数

  • sort接收二元谓词

  • sort默认按照元素的<运算符重排元素,可以根据谓词自定义排序顺序。

  • 例如:按字符串长度重排顺序

vector<string> s{"kbc", "akee", "akzz", "op"};
sort(s.begin(), s.end(), 
     [](const string &s1, const string &s2)
     {return s1.size() > s2.size();});
for (auto s : s) {
    cout << s << " ";
}
//结果为:akee akzz kbc op

②find_if算法

  • find_if算法接收一元谓词
  • find_if的调用返回第一个满足给定条件的迭代器。如果没有匹配,则返回第二个参数的迭代器。
vector<int> vec{4, 3, 9, 9};
auto it1 = find_if(vec.begin(), vec.end(), [](int a){return a > 3;});
auto it2 = find_if(vec.begin(), vec.end(), [](int a)->bool 
                  {return a > 10; });
cout << *it1 << endl; 
cout << *(it2-1) << endl;
//结果为:
4
9

③for_each算法

  • for_each算法接收一个可调用对象
  • for_each的遍历容器中的每一个元素,并对其进行指定操作。

如:打印vector<int>容器的每一个元素

vector<int> vec{1, 2, 3, 4};
for_each(vec.begin(), vec.end(), [](int a){cout << a << endl;});
//结果为:
1
2
3
4

④transform算法

  • transform算法接收一个可调用对象

  • transform算法将前两个参数接收一个输入范围,第三个参数接收一个目的迭代器地址,将输入范围的元素按定制方案传送到第三个迭代器的目的位置中

如:将序列中元素的值都替换为其绝对值

vector<int> vec{ 1, -1, -5, -10, 5 };
vector<int> vec2;
transform(vec.begin(), vec.end(), back_inserter(vec2), [](int a)   {return a < 0 ? -a : a; });
for_each(vec2.begin(), vec2.end(), [](int a) {cout << a << " "; });
//输出结果为:1 1 5 10 5
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值