STL与泛型编程<十七>:STL算法简介及for_each()算法

首先使用STL算法,有几个头文件需要说明下

#include <algorithm> //算法
#include <numeric> //数值处理
#include <functional> //仿函数或函数适配器

简介

  1. 所有的STL算法都用来处理一个或多个迭代器区间,第一个区间通常以起点和终点来表示,其他区间一般只需要提供起点就好;
  2. STL算法采用覆盖(overwrite)而非安插(insert)模式,所以调用者必须保证目标区间有足够的元素空间;
  3. 某些STL算法允许使用者传递自定的操作,以便由STL算法调用之,这些操作可以是函数,也可以是仿函数;

算法分类

  • 尾词分类
    1. 尾词_if
      如果函数有两中形式,但第一种形式的参数要求传递一个值,第二种形式要求多传递一个函数或仿函数,如find()和find_if();
    2. 尾词_copy
      带有这个尾词的算法,元素不光被操作,还被复制到目标区间,如reverse()和reverse_copy()
  • 细分(具体分类)
    • 非变动性算法(nonmodifying algorithm)
      非变动性算法既不改变元素次序,也不改变元素值,它们透过input迭代器和forward迭代器完成工作,可用于所有标准容器上,下图涵盖了STL所有非变动性算法。

      STL classes 和 string class是独立发展设计的,搜寻算法的命名很乱,比较如下图
      STL与泛型编程<十七>:STL算法简介及for_each()算法_第1张图片
    • 变动性算法
      变动性算法要不改变元素值,要不就在复制到另一空间改变元素值,若是第二种情况,原区间不会发生变化,最常用的变动性算法就是for_each()和transform(),区别如下
      1. for_each()接受一项操作,该操作可变动其参数,因此必须以by reference 方式传递。如
void square(int& elem)
{
    elem = elem * elem;
}
...
for_each(col.begin(),col.end(),  //range
        square);                //operation
  1. transform()运用某项操作,该操作返回被改动后的参数,其可以被用来将结果传递给原元素
void square(int& elem)
{
    elem = elem * elem;
}
...
transform(col.begin(),col.end(), //source range
        col.begin(),            // destination range
        square);               //operation

变动性算法如下表所示
STL与泛型编程<十七>:STL算法简介及for_each()算法_第2张图片
1. transform()的速度也许会更慢些,因它是将操作返回值返回给赋值元素,而不是直接变动元素
2. 严格的说merge()不算是变动性算法,因为它要求输入区间必须是以序(sorted)的,然而实用上merge()也可以用来合并无序区间-当然其结果也是无序的
3. 注意关联式容器的元素常被视为常数,因此不可将关联式容器当作变动性算法的目标区间
- 移除性算法
移除性算法是一种特殊的变动性算法,它们可以移除某区间内的元素,也可以在复制过程中执行移除动作。注意和变动性算法一样,其目标区间也不能是关联容器见下图
STL与泛型编程<十七>:STL算法简介及for_each()算法_第3张图片
注意移除算法只是在逻辑上移除元素,手段是:将不需被移除的元素往前覆盖(overwrite)应被移除的元素
- 变序性算法
透过元素值的赋值和交换,改变元素顺序 (但不改变元素值),和变动性算法一样,不能以关联式容器作为目标

- 排序算法
排序算法是一种特殊的变序性算法,但比一般的变序性算法复杂,花费更多时间,需要动用随机存取迭代器。如下
STL与泛型编程<十七>:STL算法简介及for_each()算法_第4张图片
(1) sort()内部采用quicksort算法,复杂度为n*log(n),但最差的情况也可能是n*n
(2)partial_sort()内部采用heap_sort算法,因此它在任何情况下保证n*log(n)复杂度。大多数情况下heapsort比quicksort慢2~5倍,所以大多数情下虽然partial_sort()具有较佳复杂度,但sort()具有较好的执行效率。partial_sort()还有一种特殊能力:如果只需要前n个元素排序,它可以在完成任务后立刻停止,
(3)stable_sort()内部采用mergesort。它对所有元素进行排序。
如果只需排序后的第n个元素,或只需要令最先或最后的n个元素(无序)就位,可以使用nth_element()。

  • 已序区间算法
    已序区间算法指的是所作用的区间在某种排序准则下已序,如下图
    STL与泛型编程<十七>:STL算法简介及for_each()算法_第5张图片
  • 数值算法
    数值算法以不同方式组合数值元素,如下图

for_each()算法

  • 声明
template<class InputIterator, class Function>
Function for_each(InputIterator first, InputIterator last, Function fn)
{
    while (first!=last)
    {
        fn (*first);
        ++first;
    }
     return fn;
}
  1. 对区间[beg,end)的每个元素调用op(elem)
  2. 返回值:返回op的副本,见下例子3
  3. op的任何返回值都会被忽略
  4. 复杂度:线性,调用op共numberOfElements次
    for_each()使用临时对象op(一个仿函数),针对每一个元素调用op(*act),如果for_each()函数的第三参数是一般函数,就以*act为参数调用之。如果第三个参数是个仿函数,则以*act为参数,调用仿函数op的operator()
    例子1,打印所有元素
#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

template<typename T>
inline void INSERT_ELEMENTS(T& col, int first, int last)
{
    for (int i=first; i<=last; ++i)
        col.insert(col.end(),i);    
}

void print(int elem)
{
    cout << elem << ' ';    
}

int main(void)
{
    vector<int> col;
    INSERT_ELEMENTS(col,1,9);
    for_each(col.begin(), col.end(), print);

    return 0;
}

例子2展示如何利用仿函数来改变一个元素内容

#include <iostream>
#include <algorithm>
#include <vector>
#include <iterator> 
using namespace std;

template<typename T> //仿函数
inline void INSERT_ELEMENTS(T& col, int first, int last)
{
    for (int i=first; i<=last; ++i)
        col.insert(col.end(),i);    
}

template<typename T>  //仿函数 
class AddValue
{
    private:
        T theValue;
    public:
        AddValue(const T& v) : theValue(v) {}
        void operator() (T& elem) const //注意参数是引用 
        {
            elem += theValue;
        }
};

int main(void)
{
    vector<int> col;
    INSERT_ELEMENTS(col,1,9);
    copy(col.begin(),col.end(),ostream_iterator<int>(cout," "));//1 2 3 4 5 6 7 8 9
    cout << endl;

    for_each(col.begin(), col.end(), AddValue<int>(10));
    copy(col.begin(),col.end(),ostream_iterator<int>(cout," "));//11 12 13 14 15 16 17 18 19
    cout << endl;

    for_each(col.begin(), col.end(), AddValue<int>(*col.begin()));
    copy(col.begin(),col.end(),ostream_iterator<int>(cout," "));//22 23 24 25 26 27 28 29 39
    cout << endl;

    return 0;
}

class AddValue<>定义了一个仿函数,使用仿函数的好处是:可以在执行期间,可以在执行期间传入欲加的数值

例子3展示如何利用for_each()的返回值。由于for_each()能返回一项操作,利用这一项特性,在该项操作中处理返回结果。

#include <iostream>
#include <algorithm>
#include <vector>
#include <iterator>
using namespace std;

template<typename T>
inline void INSERT_ELEMENTS(T& col, int first, int last)
{
    for (int i=first; i<=last; ++i)
        col.insert(col.end(),i);    
}

class MeanValue
{
    private:
        long num;
        long sum;
    public:
        MeanValue() : num(0), sum(0){} 
        void operator() (int elem) 
        {
            num++;
            sum += elem;
        }
        operator double()
        {
            return static_cast<double>(sum) / static_cast<double>(num);
        }
};

int main(void)
{
    vector<int> col;
    INSERT_ELEMENTS(col,1,8);
    double mv = for_each(col.begin(), col.end(), MeanValue());
    copy(col.begin(),col.end(),ostream_iterator<int>(cout," ")); // 1 2 3 4 5 6 7 8
    cout << endl;
    cout << "mean value: " << mv << endl;// 4.5

    return 0;
}

/* 返回操作就是求平均值,然后利用 重载double()来转换 */

你可能感兴趣的:(STL与泛型编程<十七>:STL算法简介及for_each()算法)