关于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. linux系统中,查看当前系统中,都在监听哪些端口

    需求描述: 查看当前系统中都监听着哪些的端口,用netstat命令,在此记录下 操作过程: 1.查看系统中都在监听哪些端口 [root@testvm home]# netstat -ntl Activ ...

  2. 如何创建 SVN 服务器,并搭建自己的 SVN 仓库

    SVN是一个广泛使用的版本控制系统,在日常工作中有广泛的应用空间,比如文档管理,我们就可以搭建一个 SVN 仓库,日常需要归类保存的文档统统上传到仓库中,那如何创建 SVN 服务器, 又如何搭建 SV ...

  3. ASP------<iframe>标签如何去掉滚动条

    代码: <iframe src="../Home/Thanks" width="100%" height="500" scrollin ...

  4. GIS-ArcGIS 与 ThreeJs交互联动

    一.从GIS触发Three场景 MapFeatureLayer.on("click", function (evt) { graphicsLayerOfMouse.clear(); ...

  5. 转的:burp suite小例子

    Web安全测试时经常会遇到一些蹩脚的注射点,而因各种原因利用注射又无法获取网站管理账号或拥有网站管理权限却迟迟不能upload一个shell的时候,可能会权衡一下web权限与数据库信息,哪个是我们所需 ...

  6. INSERT INTO 语句

    语法 INSERT INTO 表名称 VALUES (值1, 值2,....) 我们也可以指定所要插入数据的列: INSERT INTO table_name (列1, 列2,...) VALUES ...

  7. Android英文文档翻译系列(4)——PopupWindow

    public class PopupWindow extends Object //直接继承至Object java.lang.Object ↳ android.widget.PopupWindow  ...

  8. java框架---->quartz整合spring(一)

    今天我们学习一下quartz的定时器的使用.年轻时我们放弃,以为那只是一段感情,后来才知道,那其实是一生. quartz的简单实例 测试的项目结构如下: 一.pom.xml中定义quartz的依赖 & ...

  9. 【文智背后的奥秘】系列篇——分布式爬虫之WebKit

    版权声明:本文由文智原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/139 来源:腾云阁 https://www.qclou ...

  10. windows环境下最简单的nginx + tomcat负载均衡配置示例

    后端是两台tomcat服务器,我们简称为node1 和node2,访问地址分别是 http://192.168.1.2:8080 和 http://192.168.1.4:8080 前端使用nginx ...