5 算法
~~~~~~~
5.1 确保目标区间足够大或者在算法执行时可以增加大小
===================================================
1. 在算法中,要往Container中插入新数据,需要使用插入迭代器(比如,从back_inserter、front_inserter或inserter返回的迭代器之一)。
2. reserve只增加容器的容量:容器的大小仍然没有改变。
即使调用完reserve,当你想要让容器把新元素加入到vector或string时,你也必须对算法使用插入迭代器.
- vector<int> values; // 同上
- vector<int> results;
- ...
- results.reserve(results.size() + values.size()); // 同上
- transform(values.begin(), values.end(), // 写入transmogrify的结果
- results.end(), // 到未初始化的内存
- transmogrify); // 行为未定义!
- /*
- 在这段代码中,transform愉快地试图对results尾部的原始的、未初始化的内存赋值。
- 通常,这会造成运行期错误,因为赋值只在两个对象之间操作时有意义,而不是在一个对象和一块原始的比特之间。
- 即使这段代码碰巧作了你想要它做的事情,results也不会知道transform在它的未使用容量上“创造”的新“对象”。
- 直到results知道之前,它的大小在调用transform后仍然和原来一样。
- 同样的,它的end迭代器仍和调用transform前指向同样的位置。
- 结论呢?使用reserve而没有用插入迭代器会在算法内部导致未定义行为,也会弄乱容器。
- */
3. 但有时候你要覆盖现有容器的元素而不是插入新的。当这种情况时,你不需要插入迭代器,但你仍然需要按照本条款的建议来确保你的目的区间足够大。
可以使用resize来确保容器中有足够的元素
*注意:resize和reserve之间的区别*
5.2 选择合适的排序算法
=======================
1. partial_sort(itrBeg,itrSortEnd,itrEnd)
对[itrBeg,itrEnd)之间的元素进行排序,但只有[itrBeg,itrEnd]内的元素处于有序状态.
适用于从整个序列中抽取最好的前N个元素进行排序
2. nth_element(itrBeg,itrNth,itrEnd)
对区间[itrBeg,itrEnd)进行排序,保证itrNth之前的所有元素都小于itrNth,itrNth之后的元素都大于itrNth
适用于从整个序列中抽取最好的前N个元素,但不用进行排序.
也可用来找出排在第n位的元素
3. stable_sort(itrBegin,itrEnd)
排序时,相等元素的位置不变----此为稳定性
4. stable_partition/partition(itrBegin,itrEnd,bool判别函数)
返回一个iterator,在这个itr之前所有判别函数返回true,在itr之后所有的判别函数是false
5. 唯一我们可能会但不能使用sort、stable_sort、partial_sort或nth_element的容器是list
但list通过提供sort成员函数做了一些补偿。
6. 算法sort、stable_sort、partial_sort和nth_element需要随机访问迭代器
而partition和stable_partition只需要双向迭代器
5.3 如果你真的想删除东西的话,就在类似remove的算法后接上erase
=============================================================
1. remove不做什么?
remove接收指定它操作的元素区间的一对迭代器.它不接收一个容器.
而唯一从容器中除去一个元素的方法是在那个容器上调用一个成员函数,而且因为remove无法知道它正在操作的容器,所以remove不可能从一个容器中除去元素.
也就是说从一个容器中remove元素不会改变容器中元素的个数.
2. remove做了什么?
remove移动指定区间中的元素直到所有"不删除的"元素在区间的开头(相对位置和原来它们的一样).
它返回一个指向最后一个"不删除的"元素的迭代器的下一个位置.返回值是区间的"新逻辑终点".
3. remove与partition的区别
一般来说,调用完remove后,从区间中删除的值可能是也可能不在区间中继续存在.(返回的迭代器所指的元素及后面的元素无意义了)
而partition则保留所有的值不丢失
5.4 堤防在指针的容器上使用类似remove的算法
===========================================
1. 对指针的容器上使用remove算法,被删除的指针元素会被覆盖但是却不被删除,这就造成资源泄漏.
2. 对于计数引用的智能指针,可以安全地使用remove算法.
5.5 注意哪个算法需要有序区间
=============================
1. 只能操作有序数据的算法表:
* binary_search
* lower_bound
* upper_bound
* equal_range
* set_union
* set_intersection
* set_difference
* set_symmetric_difference
* merge
* inplace_merge
* includes
2. 下面的算法一般用于有序区间,虽然它们不要求:
* unique
* unique_copy
3. unique从一个区间除去元素的方式和remove一样,也就是说它只是区分出不除去的元素和要删除的数据,而并不能真正进行删除操作.
4. 注意区间的排序方法和算法的排序方法要保持一致.
例如binary_search默认为升续排列,就不能用于降续排列的区间中.
5.6 通过mismatch或lexicographical比较实现简单的忽略大小写字符串比较
====================================================================
5.7 了解copy_if的正确实现
==========================
1. 不正确的实现:
- template<typename InputIterator, // 一个不很正确的
- typename OutputIterator, // copy_if实现
- typename Predicate>
- OutputIterator copy_if(InputIterator begin,
- InputIterator end,
- OutputIterator destBegin, Predicate p)
- {
- return remove_copy_if(begin, end, destBegin, not1(p));
- }
然而
not1不能直接应用于一个函数指针,函数指针必须先传给ptr_fun。
要调用这个copy_if实现,你必须传递的不仅是一个函数对象,而且是一个可适配的函数对象。
这够简单了,但是想要成为STL算法用户的人应该不必这样。
标准STL算法从来不要求它们的仿函数是可适配的,copy_if也不应该要求。
上面的实现是好的,但不够好。
2. 正确的实现为
- template<typename InputIterator, // 一个copy_if的
- typename OutputIterator, // 正确实现
- typename Predicate>
- OutputIterator copy_if(InputIterator begin,
- InputIterator end,
- OutputIterator destBegin,
- Predicate p) {
- while (begin != end) {
- if (p(*begin))*destBegin++ = *begin;
- ++begin;
- }
- return destBegin;
- }
5.8 用accumulate或for_each来获取自定义的区间统计信息
=====================================================
1. accumulate不存在于orithm>。取而代之的是,它和其他三个“数值算法”都在<numeric>中。(那三个其它的算法是inner_product、adjacent_difference和partial_sum。)
2. accumulate存在两种形式。
* 带有一对迭代器和初始值的形式可以返回初始值加由迭代器划分出的区间中值的和
* accumulate的另一种形式,带有一个初始值,迭代器区间与一个任意的统计函数,算法会把初值与一个元素作为统计函数的两个参数,并将结果作为下一个元素的初值,重复调用统计元素直到迭代器结束
3. accumulate直接返回那些我们想要的统计值,而for_each返回一个函数对象