C++学习笔记:有用的算法

来源:互联网 发布:js获取焦点文字 编辑:程序博客网 时间:2024/04/28 16:28

标准库包含一组称为算法的函数,这些算法用来简化很多涉及在连续数据上应用重复操作的任务。

搜索:

标准算法包含多种风格的搜索,它们分为两大类:线性搜索与二分搜索。线性搜索从头开始检查一个区间内的所有元素,不断处理后续元素,直到达到末端为止(或因为搜索成功而终止)。二分搜索要求元素使用<操作符或按照自定义的谓词(即返回布尔值的函数或对象)进行升序排序。

线性搜索算法:

最基本的线性搜索函数是find。它在迭代器的区间内搜索一个值,返回指向该区间内首次匹配到目标元素的迭代器。如果find没有找到匹配项,则返回末端迭代器的副本。

find算法的搭档是find_if。find_if不搜索匹配的值,而是接受一个谓词函数或函数对象。它对区间内的每个元素调用该函子,直到函子返回真值为止。如果函子始终为返回真值,则find_if返回末端迭代器。

假如你要对一个整数向量进行搜索,找到所有处于特定区间内的值,而非单独的一个值。一种好的解决方案是编写一个可以将整数与任意区间进行比较的通用目函子。使用这个函子时,可以讲区间界限作为构造函数的参数传入。下面是一个实现intrange函子的例子:(作为锦上添花的功能,作者允许调用者以任意顺序指定区间界限)(data.hpp在最后,主要完成数据流的读写操作)

#ifndef INTRANFE_HPP_
#define INTRANGE_HPP_

#include<algorithm>

class intrange
{
public:
    inline intrange(int low, int high);
    inline bool operator()(int test)const;
private:
    int const low_;
    int const high_;
};

inline intrange::intrange(int low, int high)
    : low_(std::min(low, high)), high_(std::max(low, high))
{}

inline bool intrange::operator()(int test)
    const
{
    return (test >= low_ && test <= high_);
}

#endif

下面是一个测试程序,利用find_if查找首个处于【10,20】之间的整数:

#include <algorithm>
#include <iostream>
#include <ostream>

#include "data.hpp"
#include "intrange.hpp"

int main()
{
  intvector data;
  read_data(data);
  write_data(data);
  intvec_iterator iter(std::find_if(data.begin(), data.end(), intrange(10, 20)));
  if (iter == data.end())
    std::cout << "No values in [10,20] found\n";
  else
    std::cout << "Value " << *iter << " in range [10,20].\n";
}

search函数与find类似,不同之处在于它搜索的是匹配的子区间。也就是说,需要为它提供一个待搜索的迭代器区间和一个待匹配的迭代器区间。search算法查找首次出现的、与待匹配区间完全相同的元素序列。

二分搜索算法:

binary_search函数仅测试排序后的区间内是否包含某个特定的值。默认情况下它仅使用<操作符进行比较。binary_search的另一种形式则以额外参数的形式接受一个比较函子,由它执行比较。

lower_bound函数除了返回的是迭代器之外,其他与binary_search类似。返回的迭代器指向搜索值第一次出现的位置;如果要在向量中插入该值,并保持排序,则迭代器指向待插入的位置。upper_bound与lower_bound函数类似,只不过它返回的迭代器指向的是可以在保持排序的情况下插入该值的最后一个位置;如果找到了这值,upper_bound将指向向量中该值最后一次出现后的下一个位置。

其他有用的线性函数还有count,它接受一个迭代器区间以及一个值,返回这个值在这个区间内出现的次数。与之对应的count_if函数用谓词取代特定的值,返回谓词取得真值的次数。

min_element函数接受一个区间,返回指向区间内最小元素的迭代器。与之对应的有max_element函数,它返回指向区间内最大元素的迭代器。二者都包含常规的重载形式:一种重载使用<操作符,另一种重载将比较谓词作为额外参数。

比较:

可以调用equal函数检查两个区间是否相等,即它们是否包含相同的值。该算法接受一个区间的起始迭代器和超出末端的下一位置的迭代器,以及另一个区间的起始迭代器,并假设两个区间的长度相等。如果两个区间内的所有元素都相等则返回真,如果有任何元素不匹配则返回假。该函数有两种形式:一种只将迭代器传入equal,使用==操作符比较元素;另一种将比较函子作为最后一个参数传入,equal调用该函子比较元素。函子的第一个参数是第一个区间的元素,第二个参数是第二个区间的元素。

mismatch函数则相反。它对两个区间进行比较,返回指向首对不匹配元素的std::pair结构。pair中第一个迭代器指向第一个区间中的元素,第二个迭代器指向第二个区间中的元素。如果两个区间相等,则返回一对末端迭代器。

lexicographical_compare创造了最长算法名的记录。它对两个区间进行比较,,确定第一个区间是否小于第二个区间。它通过一次比较一个元素来完成对区间的比较。如歌两个区间相等则返回假。如果一个区间到达末尾之前的元素都与另一个区间相等,而后者更长,则短的区间小于长的区间。如果找到了不匹配元素,则包含较小元素的区间是较小的区间。所有元素均使用<操作符(或调用方提供的谓词)进行比较,检查它们是否等价而非是否相等。

重组织数据:

merge算法将两个排序的输入区间合并为一个单独的输出区间。每次使用时,必须保证输出区间有足够的空间来容纳来自两个输入区间的整体合并结果。两个输入区间的长度可以不同,因此merge需要接受5个或6个参数:两个用于第一个输入区间,两个用于第二个输入区间,一个表示输出区间的起始,还有一个可选的参数表示用来替代<操作符的函子。

replace算法扫描输入区间,将其中出现的每个旧值替换为一个新值。替换在原地进行,因此需要用一对常规的迭代器而非写迭代器说明区间。replace_if与之类似,但接受谓词而非旧值。

generate算法重复地调用一个不带参数的函子,将返回值赋值到输出区间。该算法对区间内的每个元素调用一次函子,并覆盖这些元素。

transform算法通过对输入区间的每个数据项调用函子,实现对数据项的修改。它包括四个实参:前两个指定输入范围,第三个实参是一个输出迭代器,最后的实参是一个函数名。

删除元素:

最棘手的算法是那些“移除”元素的算法。类似于remove的算法事实上并不删除任何数据,而是重新组织区间内的元素,使得标记为移除的元素聚集在区间的末尾。此后你可以决定使用需要保留的元素自区间,或者通过调用erase成员数函数清除“移除掉的”元素。

remove函数接受一个迭代器区间与一个值,移除所有等于该值的元素。还可以使用谓词形式的remove_if移除所有使谓词为真的元素。这两个函数均有对应的复制形式,它们不重新组织任何数据,而只对不被移除的元素进行复制:remove_copy复制所有不等于某个值的元素,而remove_copy_if复制所有使谓词返回假值的元素。

我们可能经常会遇到复制满足特定条件的元素的情况。理想情况下,可以为条件编写一个谓词并调用copy_if算法,但问题在于标准库中没有copy_if。因此可以逆转逻辑,改为调用remove_copy_if复制所有是谓词返回假值的元素。

另一种移除元素的算法是unique(以及unique_copy ).他接受一个输入区间,移除其中所有邻接的重复元素,由此确保区间内的每个数据项都是唯一的。这两个函数都可以接受一个比较函子,用以取代默认使用的==操作符。

下面是一个清除元素的例子,读取整数向量,清除所有等于0的元素,仅将其中处于【24,42】之间的元素复制到另一个向量中,并对另一个向量进行排序,移除其中的重复项,最后打印结果向量。

#include <algorithm>
#include "data.hpp"
#include "intrange.hpp"

class outofrange
{
public:
  outofrange(int low, int high) : range_(low, high) {}
  bool operator()(int test) const { return not range_(test); }
private:
  intrange range_;
};

int main()
{
  intvector data;
  read_data(data);
  data.erase(std::remove(data.begin(), data.end(), 0), data.end());
  intvector copy;
  std::remove_copy_if(data.begin(), data.end(), std::back_inserter(copy),
                      outofrange(24, 42));
  std::sort(copy.begin(), copy.end());
  copy.erase(std::unique(copy.begin(), copy.end()), copy.end());
  write_data(copy);
}

data.hpp:

#ifndef DATA_HPP_
#define DATA_HPP_

#include <algorithm>
#include <iostream>
#include <iterator>
#include <ostream>
#include <vector>

typedef std::vector<int> intvector;
typedef intvector::iterator intvec_iterator;

template<class Container>
void read_data(Container& data)
{
  data.clear();
  data.insert(data.begin(), std::istream_iterator<typename Container::value_type>(std::cin),
              std::istream_iterator<typename Container::value_type>());
}

template<class Container>
void write_data(Container const& data)
{
  std::cout << "{ ";
  std::copy(data.begin(), data.end(),
            std::ostream_iterator<typename Container::value_type>(std::cout, " "));
  std::cout << "}\n";
}
#endif

原创粉丝点击