关于STL算法需要注意的是:

(1) 所有STL算法被设计用来处理一个或多个迭代器区间。第一个区间通常以起点和终点表示,至于其他区间,多数情况下只需提供起点即可,其终点可自动以第一区间的元素数推导出来,故调用者必须确保区间的有效性。

(2) STL算法采用覆盖模式(overwrite),而非安插模式(insert).所以调用者必须保证目标区间拥有足够的元素区间。

(3) 所有的算法处理的都是半开区间[begin,end)。

下面逐一介绍一些常用的算法:

for_each算法

for_each(InputIterator beg, InputIterator end, UnaryProc op)

  • 对区间[beg,end)每一个元素调用op(elem)

  • 返回op的一个副本

  • op的任何返回值都会被忽略

  • 复杂度:线性。调用op numberOfElement次。

      void print(int elem)
    {
    cout << elem << " ";
    } template<typename T>
    struct AddValue
    {
    private:
    T _theValueToAdd;
    public:
    AddValue(const T & v)
    : _theValueToAdd(v)
    { } void operator() (T & elem)
    {
    elem += _theValueToAdd;
    }
    }; //求平均值
    struct MeanValue
    {
    public:
    MeanValue()
    : _num_of_elements(0)
    , _sum(0)
    { }
    void operator() (int elem)
    {
    _num_of_elements++;
    _sum += elem;
    } //重载double类型运算符,没什么道理,只是返回平均值
    operator double()
    {
    return (static_cast<double>(_sum) / static_cast<double>(_num_of_elements));
    }
    private:
    long _num_of_elements;
    long _sum;
    }; //for_each算法
    vector<int> v;
    std::generate_n(std::back_inserter(v),10,rand);
    std::copy(v.begin(),v.end(),std::ostream_iterator<int>(std::cout," "));
    cout << endl; //一般函数作为for_each的参数
    std::for_each(v.begin(),v.end(), print); //函数对象作为for_each的参数
    //给v所有元素都加上100
    std::for_each(v.begin(),v.end(),AddValue<int>(100)); cout << endl;
    std::copy(v.begin(),v.end(),std::ostream_iterator<int>(std::cout," ")); //for_each的返回值 for_each返回op(一再算法内部被变动过的)一个副本
    //返回应该是函数对象,但是被转换成了double,这个是隐式转换
    double mv = std::for_each(v.begin(),v.end(),MeanValue());
    cout << "\nMeanValue: " << mv << endl;

transform 算法

transform提供两种能力:

(1) 4个参数形式。把源区间的元素转换到目标区间,即复制与修改一气呵成。

OutputInterator
tansform (InputIterator sourceBeg, InputInterator sourceEnd,
OutputIterator destBeg, UnaryFunc op)
  • 针对源区间[sourceBeg,sourcEnd)调用op(elem),并将结果写到destBeg起始的目标区间。
  • 调用者必须确保目标区间有足够的空间。
  • sourceBeg与destBeg可以相同,所以,和for_each一样,可以使用transform来变动某一序列内的元素。transform的速度稍许些慢,因为它是将操作返回值赋值给元素,而不是直接变动元素。

(2) 5个参数形式,将前两个源序列中的元素合并,并将结果写入目标区间。即将两序列的元素结合。

OutputIterator
transform(InputIterator1 source1Beg, InputIterator1 source1End,
InputIterator2 source2Beg,
OutputIterator destBeg,
BinaryFunc op)
  • 针对第一个源区间[source1Beg,source1End)以及从source2Beg开始的第二个源区间的相应元素,调用op(source1Elem,source2Elem),并将结果写入以destBeg起始的目标区间。

transform与for_each的区别

for_each()接受一项操作,该操作返回被改动后的参数。因此该参数必须以by reference方式传递。

transform()运用某项操作,该操作返回被改动后的参数。

transform使用实例:

string str("acCDeFG");
//transform典型应用 ,string大小写转换
std::transform(str.begin(),str.end(),str.begin(),toupper); template<typename T>
struct Add
{
Add(const T & v)
: _theValueToAdd(v)
{
}
T operator() (const T & elem)
{
return (_theValueToAdd + elem);
}
T _theValueToAdd;
};
//使用transform实现上述for_each的AddValue功能
std::transform(v.begin(),v.end(),v.begin(),Add<int>(1000)); int arr[] = {10,1,2,4,4,7};
list<int> list1(arr,arr + sizeof(arr) / sizeof(arr[0]));
std::copy(list1.begin(),list1.end(),std::ostream_iterator<int>(std::cout," ")); //实现对list中每个数值求平方
std::transform(list1.begin(),list1.end(),list1.begin(),list1.begin(),std::multiplies<int>());

互换元素内容 swap_ranges

//将区间[beg1, end1)内的元素和从beg2开始的区间内的对应元素互换
//返回第二个区间中最后一个被交换元素的下一位置
ForwardIterator2
swap_ranges (ForwardIterator1 beg1, ForwardIterator1 end1,
ForwardIterator2 beg2)

注:如果要将相同型别的两个容器的全部元素互换,应该使用swap()成员函数,swap性能优异,她只交换容器的内部数据,即交换某些内部指针,所有时间复杂度为常数。

非变动性算法

(1) 元素计数

//计算区间[beg, end)中元素值等于value的元素个数
difference _type
count (InputIterator beg, InputIterator end, const T& value) //计算区间[beg, end)中令op(elem)元素判断结果为true的元素个数
difference _type
count_if (InputIterator beg, InputIterator end, UnaryPredicate op) int arr[] = {1,2,1,3,4,8,9,8};
vector<int> v(arr, arr + sizeof(arr)/sizeof(arr[0])); //元素计数 求vector中大于com_num的元素个数
int comp_num = 4;
int num = std::count_if(v.begin(),v.end(),std::bind(std::greater<int>(),std::placeholders::_1,comp_num));

(2) 最大值最小值

如果存在多个最大值或最小值,算法返回找到的第一个。

//返回区间[beg, end)中最小值的位置,注意返回的是位置,你要通过 operator* 来得到值
// 以operator< 进行元素比较
InputIterator
min_element (InputIterator beg, InputIterator end) //Op用来比较两个元素,op(elem1, elem2)
InputIterator
min_element (InputIterator beg, InputIterator end, CompFunc op) //返回区间[beg, end)中最大值的位置
InputIterator
max_element (InputIterator beg, InputIterator end) InputIterator
max_element (InputIterator beg, InputIterator end, CompFunc op)

(3) 搜索元素

搜索第一个匹配元素

//返回区间[beg, end)中第一个元素值等于value的元素位置,如果没有找到,两种形式都返回end
InputIterator
find (InputIterator beg, InputIterator end, const T& value) InputIterator
find_if (InputIterator beg, InputIterator end, UnaryPredicate op) //搜寻元素 find 已序区间使用 binary_search
//关联式容器使用成员函数find()
std::find(v.begin(),v.end(),4);

(4) 区间比较

//判断区间[beg, end)内的元素是否都和以cmpBeg开头的区间内的元素相等
bool
equal (InputIterator1 beg, InputIterator1 end,
InputIterator2 cmpBeg) //判断区间[beg, end)内的元素和以cmpBeg开头的区间内对应的元素,
//是否都能够使op(elem, cmpElem)为true
bool
equal (InputIterator1 beg, InputIterator1 end,
InputIterator2 cmpBeg, BinaryPredicate op) //区间比较equal 典型应用 不区分大小写比较字符串 string
struct NoCaseCompareStr
{
bool operator() (char c1, char c2) const
{
return (toupper(c1) == toupper(c2));
}
}; string src("abCD");
string dst("abcd");
bool isEqual = (src.size() == dst.size()
&& std::equal(src.begin(),src.end(),dst.begin(),NoCaseCompareStr()));

移除性算法

注:移除性算法是在一个区间内移除某些元素。这些算法并不能改变元素的数量,只是以逻辑上的思考,将原本置于后面的“不移除的元素”向前移动,覆盖那些被删除的元素而已,返回新区间的逻辑终点。

//移除区间[beg, end)中每一个与value相等的元素
ForwardIterator
remove (ForwardIterator beg, ForwardIterator end,
const T& value) //移除区间[beg, end)中每一个令op(elem)为true的元素
ForwardIterator
remove_if (ForwardIterator beg, ForwardIterator end,
UnaryPredicate op) int arr[] = {1,2,1,3,4,8,9,8};
vector<int> v(arr, arr + sizeof(arr)/sizeof(arr[0]));
//删除所有大于4的元素
std::function<bool(int)> func = std::bind(std::greater<int>(),std::placeholders::_1,4);//绑定函数对象
//移除性算法只是返回一个新的终点,想把元素真正删除,还需要调用erase成员函数
v.erase(std::remove_if(v.begin(),v.end(),func),v.end());

注:

(1) 无法在关联式容器上使用移除型算法

(2) list提供了等效的成员函数remove,效率更高。

排序算法

关联式容器自动排序,但对全体元素进行一次性排序,通常比始终维护它们保持已序状态来得高效一些。

(1) 对所有元素排序

//排序,缺省使用 operator< 排序
void
sort (RandomAccessIterator beg, RandomAccessIterator end) //排序,使用op(elem1, elem2)准则
void
sort (RandomAccessIterator beg, RandomAccessIterator end,
BinaryPredicate op) //保证相等元素的原本相对次序在排序后保持不变
//换句话出,相等的元素它会按照固有的顺序,比如字典顺序排序
void
stable_sort (RandomAccessIterator beg, RandomAccessIterator end) void
stable_sort (RandomAccessIterator beg, RandomAccessIterator end,
BinaryPredicate op) //降序排列
std::sort(v.begin(),v.end(),std::greater<int>());

注:

不可以对list调用这些算法,list不支持随机存取迭代器,可以使用list自己提供的成员函数sort().

(2) 局部排序

//以 < 对区间[beg, end)内的元素进行排序,使区间[beg, sortEnd)内的元素有序
void
partial_sort (RandomAccessIterator beg,
RandomAccessIterator sortEnd,
RandomAccessIterator end) //以op(elem1, elem2)对区间内元素进行排序
void
partial_sort (RandomAccessIterator beg,
RandomAccessIterator sortEnd,
RandomAccessIterator end,
BinaryPredicate op)

和sort不同,partial_sort并不对全部元素排序:一旦第一个元素至sortEnd之前的所有元素都排拖次序,就立即停止。

对sortEnd-beg个元素进行排序,也就是说,如果sortEnd-beg等于42,则该函数将有序次序中的最小值元素放在序列中的前42个位置。partial_sort完成之后,从beg到sortEnd(但不包括sortEnd)范围内的元素时有序的,已排序范围内没有元素大于sortEnd之后的元素。未排序元素之间的次序是未指定的。

(缺省升序排列)

例:

int arr[] = {1,2,1,3,4,8,9,8};
vector<int> v(arr, arr + sizeof(arr)/sizeof(arr[0])); vector<int> v1(v);
vector<int> v2(v); PrintElements(v1,"V1 and V2");
//局部排序 取前3个排序元素
std::partial_sort(v1.begin(),v1.begin() + 3,v1.end(),std::greater<int>());
PrintElements(v1,"PartialSort");
//全排序
std::sort(v2.begin(),v2.end(),std::greater<int>());
PrintElements(v2,"AllSort");

备注:

sort内部采用quicksort算法

partial_sort内部采用heapsort算法

stable_sort内部采用mergesort算法

(3) 根据第n个元素排序

如果只需要排序后的第n各元素,或只需令最先或最后的n个元素(无序)就位,这个算法就很有用。

//对区间[beg, end)内的元素进行排序,使第n个位置上的元素就位
//也就是n之前的元素都小于等于它,n之后的元素都大于等于它
//但是n前后的元素不要求有序
void
nth_element (RandomAccessIterator beg,
RandomAccessIterator nth,
RandomAccessIterator end) void
nth_element (RandomAccessIterator beg,
RandomAccessIterator nth,
RandomAccessIterator end,
BinaryPredicate op)

类似的算法parttition,根据某个准则,将序列中的元素分为两部分。

//将区间[beg, end)中使op(elem)为true的元素向前移动
BidirectionalIterator
partition (BidirectionalIterator beg, BidirectionalIterator end,
UnaryPredicate op) //会保持它们之前的相对次序
BidirectionalIterator
stable_partition (BidirectionalIterator beg, BidirectionalIterator end,
UnaryPredicate op)

nth_elementpartition的区别:

nth_element调用之后,并不精确知道第一子集和第二子集之间有什么不同,两部分都可能包含和第n各元素相等的元素。

partiton调用之后,并不知道第一和第二子集内各有多少个元素。返回的pos指出第二个子集的起点,第二子集的所有元素都不满足上述传入的准则。

已序区间算法

针对已序区间的算法,前提是源区间必须在某个排序准则下已序,有一定的性能优势。

即使迭代器并非随机存取型,也可以使用这些算法,此时算法的复杂度会降为线性。

//判断已序区间[beg, end)是否包含和value等值的元素
bool
binary_search (ForwardIterator beg, ForwardIterator end,
const T& value) bool
binary_search (ForwardIterator beg, ForwardIterator end,
const T& value,
BinaryPredicate op)

//返回第一个"大于等于value"的元素位置,如果不存在返回end
ForwardIterator
lower_bound (ForwardIterator beg, ForwardIterator end,
const T& value) ForwardIterator
lower_bound (ForwardIterator beg, ForwardIterator end,
const T& value,
BinaryPredicate op) //返回第一个"大于value"的元素位置
ForwardIterator
upper_bound (ForwardIterator beg, ForwardIterator end,
const T& value) ForwardIterator
upper_bound (ForwardIterator beg, ForwardIterator end,
const T& value,
BinaryPredicate op) //返回"与value相等"的元素所形成的区间
pair<ForwardIterator,ForwardIterator>
equal_range (ForwardIterator beg, ForwardIterator end,
const T& value) pair<ForwardIterator,ForwardIterator>
equal_range (ForwardIterator beg, ForwardIterator end,
const T& value,
BinaryPredicate op)

//source1: 1 2 2 4 6 7 7 9
//source2: 2 2 2 3 6 6 8 9
//Sum: 1 2 2 2 2 2 3 4 6 6 6 7 7 8 9 9
//将源区间[source1Beg, source1End)和[source2Beg, source2End)内的元素合并
//以destBeg为起点输出
OutputIterator
merge (InputIterator source1Beg, InputIterator source1End,
InputIterator source2Beg, InputIterator source2End,
Output Iterator destBeg) OutputIterator
merge (InputIterator source1Beg, InputIterator source1End,
InputIterator source2Beg, InputIterator source2End,
OutputIterator destBeg,
BinaryPredicate op)

STL学习笔记--算法的更多相关文章

  1. Effective STL 学习笔记 Item 34: 了解哪些算法希望输入有序数据

    Effective STL 学习笔记 Item 34: 了解哪些算法希望输入有序数据 */--> div.org-src-container { font-size: 85%; font-fam ...

  2. Effective STL 学习笔记 31:排序算法

    Effective STL 学习笔记 31:排序算法 */--> div.org-src-container { font-size: 85%; font-family: monospace; ...

  3. Effective STL 学习笔记 39 ~ 41

    Effective STL 学习笔记 39 ~ 41 */--> div.org-src-container { font-size: 85%; font-family: monospace; ...

  4. Effective STL 学习笔记 Item 38 : Design functor classes for pass-by-value

    Effective STL 学习笔记 Item 38 : Design functor classes for pass-by-value */--> div.org-src-container ...

  5. Effective STL 学习笔记 Item 30: 保证目标区间足够大

    Effective STL 学习笔记 Item 30: 保证目标区间足够大 */--> div.org-src-container { font-size: 85%; font-family: ...

  6. Effective STL 学习笔记: Item 22 ~ 24

    Effective STL 学习笔记: Item 22 ~ 24 */--> div.org-src-container { font-size: 85%; font-family: monos ...

  7. Effective STL 学习笔记 Item 21:Comparison Function 相关

    Effective STL 学习笔记 Item 21:Comparison Function 相关 */--> div.org-src-container { font-size: 85%; f ...

  8. Effective STL 学习笔记:19 ~ 20

    Effective STL 学习笔记:19 ~ 20 */--> div.org-src-container { font-size: 85%; font-family: monospace; ...

  9. Effective STL 学习笔记: 多用 vector & string

    Effective STL 学习笔记: 多用 vector & string 如果可能的话, 尽量避免自己去写动态分配的数组,转而使用 vector 和 string . 原书作者唯一想到的一 ...

随机推荐

  1. java 正则表达式验证

    package com.fsti.icop.util.regexp; import java.util.regex.Matcher; import java.util.regex.Pattern; p ...

  2. 图像识别api

    https://console-cloud.megvii.com/

  3. nodejs搭建简单web服务器!!

    var http = require("http"), url = require("url"), path = require("path" ...

  4. Dubbo(三) -- 多协议支持与多注册中心

    一.Dubbo支持的协议 Dubbo协议.Hessian协议.HTTP协议.RMI协议.WebService协议.Thrift协议.Memcached协议.Redis协议 二.协议简介 详细参考:ht ...

  5. 如何在Oculus官网下载OculusSetup.exe(当前时间20170720)

    踩着免费的蓝灯FQ登录了Oculus官网,找了半天找不到哪里下载OculusSetup.exe,最后在最下面的支持中心找到了..... https://www.oculus.com/

  6. PopupMenu弹出菜单

    CMenu MoviePopupMenu;//声明 MoviePopupMenu.CreatePopupMenu();//创建弹出菜单 根据对象类型增加弹出项 ) // FLASH对象 { CStri ...

  7. JS-JavaScript类库整理 [更新中...]

    老大.jQuery插件库 ——收集最全最新最好的jQuery插件 http://www.jq22.com/ 一.Moment.js ——JavaScript 日期处理类库 http://momentj ...

  8. getOwnPropertyNames() & keys()

    1.getOwnPropertyNames方法可以获得对象的所有属性名,并储存于一个数组当中. keys方法只能获取可遍历的属性名并储存于数组. 2.在完成notepad模块模拟的过程中使用了getO ...

  9. Android技巧分享——Android开发超好用工具吐血推荐(转)

    内容中包含 base64string 图片造成字符过多,拒绝显示

  10. C# 文件夹的常用操作

    C#获取文件夹下的所有文件的文件名 string path = @"E:\微课视频大于200M"; DirectoryInfo folder = new DirectoryInfo ...