STL(八)——算法(Algorithm)(一)

算法概观

STL算法是将最常被运用的算法规范出来,其涵盖的区间有可能在每五年一次的C++标准委员会中不断增订。广义而言,我们所写的每个程序都是一个算法,其中的每个函数也都是一个算法。STL收录了许多算法,包括排序,查找,排列组合等等的算法。STL为这些算法提供能泛化使用的版本。但要注意的是,特定的算法往往搭配特定的数据结构,例如RB-tree便是为了解决查找问题而提出的,本篇讨论的算法大多是独立于特定数据结构的。stl算法又分为质变算法(mutating algorithms)和非质变算法(nomutating algorithms)。质变算法指在运算过程中会更改区间内元素的内容的算法,比如拷贝,复制,替换等等。非质变算法则指在运算过程中不会改变区间内(迭代器所指)的元素内容,比如查找,匹配,计数等等。

算法的泛化过程

将一个叙述完整的算法转化为程序代码,并将算法独立于设计结构,不接受数据结构的羁绊,并不是简单的事情。让我们来看看算法泛化的一个实例。以简单的循序查找为例,我们的直觉是:

int* find(int* arrayHead, int arraySize, int value)
{
    for(int i = 0; i < arraySize; ++i)
    {
        if(arrayHead[i]==value)
        break;
    }
    return &(arrayHead[i]);
}

该函数在某个区间内寻找value,返回的是一个指针,指向它所找到的第一个符合条件的元素;如果没有找到,那就返回最后一个元素的下一个位置。“最后元素的下一个位置”称为end。返回end以表示“查找无结果”似乎很奇怪,为什么不返回null?因为end指针可以对其他类型的容器带来泛化效果,这是null所无法做到的。

解释一下上述代码,使用array的时候我们经常被教导不要超越array的大小,但实际上指向array元素但指针,不但可以合法地指向array内的任何位置,也可以指向array尾端以外的任何位置。只不过当指针向array以外的位置时,不能进行提领(dereference)操作。

上面的代码可以拿来使用了,但这种做法暴露了容器太多的实现细节,因此依赖于特定的容器(暴露了数组的数据结构)。为了让find()使用于所有类型,其操作应该更加抽象化。让find()接受两个指针作为参数,标示出一个操作区间是一个很好的做法:

int* find(int* begin, int* end, int value)
{
    while(begin!=end && *begin!=value)
        ++begin;
    return begin;
}

现在,由于find()并不针对特定的数据结构,只要符合上述参数的数据都能放进来使用。现在我们用模板改写这个算法:

template<typename T>
T* find(T* begin, T* end, const T& val) //改为const引用减少开销
{
    while(begin!=end &&*begin != value)
        ++begin;
    return begin;
}

现在,只要提供的指针支持以下操作,就可以使用改方法:

  • inequality操作符
  • dereference提领操作符
  • prefix increment前置递增操作符
  • copy复制行为

c++的一个有点是几乎所有东西都可以改写成程序员自定义的形式,于是我们可以编写自己的迭代器来支持以上行为。我们将算法参数改写成这样,便是一个完全泛化的find()函数。

扫描二维码关注公众号,回复: 3350674 查看本文章
template<class Iterator, class T>
Iterator find(Iterator begin, Iterator end, const T& value)
{
    while(begin != end && *begin != value)
        ++begin;
    return begin;
}

上面便是算法的泛化过程。

猜你喜欢

转载自blog.csdn.net/Pokemon_Master/article/details/82621704
今日推荐