stl sort分析

最近写代码,无意中发现了一个坑,关于自定义比较函数的stl sort函数的坑,于是记录下来。

先贴代码:


 1 #include <iostream>
2 #include <vector>
3 #include <algorithm>
4
5 struct finder
6 {
7 bool operator()(int first, int second){return first <= second;}
8 } my_finder;
9
10 int main(int argc, char** argv)
11 {
12 int value = atoi(argv[1]);
13 int num = atoi(argv[2]);
14 std::vector<int> vecTest;
15 for(int i=0; i!=num; ++i)
16 vecTest.push_back(value);
17
18 std::sort(vecTest.begin(), vecTest.end(), my_finder);
19 for(int i=0; i!=vecTest.size(); ++i)
20 std::cout<<vecTest[i]<<‘\t‘;
21 std::cout<<std::endl;
22
23 return 0;
24 }

这段代码看上去好好的,实际上却有core的可能。

且看图:

敏思苦想很久,也想不出为啥会core,后来查了资料,才发现了问题所在,现在通过源码分析一下原因。

于是定位到sort函数:


 1   template<typename _RandomAccessIterator, typename _Compare>
2 inline void
3 sort(_RandomAccessIterator __first, _RandomAccessIterator __last,
4 _Compare __comp)
5 {
6 typedef typename iterator_traits<_RandomAccessIterator>::value_type
7 _ValueType;
8
9 // concept requirements
10 __glibcxx_function_requires(_Mutable_RandomAccessIteratorConcept<
11 _RandomAccessIterator>)
12 __glibcxx_function_requires(_BinaryPredicateConcept<_Compare, _ValueType,
13 _ValueType>)
14 __glibcxx_requires_valid_range(__first, __last);
15
16 if (__first != __last)
17 {
18 std::__introsort_loop(__first, __last, __lg(__last - __first) * 2,
19 __comp);
20 std::__final_insertion_sort(__first, __last, __comp);
21 }
22 }

这是stl_algo.h中的sort函数,且忽略10-14行的参数检查,实际上sort函数先是用了introsort(内省排序,http://en.wikipedia.org/wiki/Introsort),然后采用了insertsort(插入排序)。

1、我们先来分析内省排序吧。

  先来看看__introsort_loop的函数原型


 1   template<typename _RandomAccessIterator, typename _Size>
2 void
3 __introsort_loop(_RandomAccessIterator __first,
4 _RandomAccessIterator __last,
5 _Size __depth_limit)
6 {
7 typedef typename iterator_traits<_RandomAccessIterator>::value_type
8 _ValueType;
9
10 while (__last - __first > int(_S_threshold))
11 {
12 if (__depth_limit == 0)
13 {
14 std::partial_sort(__first, __last, __last);
15 return;
16 }
17 --__depth_limit;
18 _RandomAccessIterator __cut =
19 std::__unguarded_partition(__first, __last,
20 _ValueType(std::__median(*__first,
21 *(__first
22 + (__last
23 - __first)
24 / 2),
25 *(__last
26 - 1))));
27 std::__introsort_loop(__cut, __last, __depth_limit);
28 __last = __cut;
29 }
30 }

  如果__last - __first > int(_S_threshold)的时候,就开始循环了。

  关于_S_threshold的定义:


1 enum { _S_threshold = 16 };

  好吧,是写死的,为【16】

  注意,我为什么把16标红,这就是坑开始的地方了。如果元素小于16(第10行),就直接略过,开始了:

  std::__final_insertion_sort(__first, __last, __comp);  //
本次不对其进行分析

  我们继续往下走,__depth_limit哪来的呢。看代码:


1   template<typename _Size>
2 inline _Size
3 __lg(_Size __n)
4 {
5 _Size __k;
6 for (__k = 0; __n != 1; __n >>= 1)
7 ++__k;
8 return __k;
9 }

  还记得sort调用introsort吗?

  std::__introsort_loop(__first, __last,
__lg(__last - __first) * 2, __comp);

  当__depth_limit !=
0时,则开始了introsort递归,而真正影响它的是__unguarded_partition函数。

  __unguarded_partition函数原型:


 1   template<typename _RandomAccessIterator, typename _Tp, typename _Compare>
2 _RandomAccessIterator
3 __unguarded_partition(_RandomAccessIterator __first,
4 _RandomAccessIterator __last,
5 _Tp __pivot, _Compare __comp)
6 {
7 while (true)
8 {
9 while (__comp(*__first, __pivot))
10 ++__first;
11 --__last;
12 while (__comp(__pivot, *__last))
13 --__last;
14 if (!(__first < __last))
15 return __first;
16 std::iter_swap(__first, __last);
17 ++__first;
18 }
19 }

  好吧,终于要找到原因了,就是这个__pivot了。

  还记得我们自定义的__comp函数吗?  

  struct finder
  {
    bool operator()(int first, int second){return
first <= second;}
  }
my_finder;

  当*__first ==
__pivot的时候,返回了true,然后,执行了16行的元素交换。

  那如果像我测试的代码那样,所有元素都相等呢?岂不就是走进了死循环,等着的就可能是越界了。

  为什么__unguarded_partition不检查边界呢?有人分析称是为了效率,再大数据排序的时候,每次都需要校验边界,确实也是个很大开销。

  后来看人总结:永远让比较函数对相等的值返回false(来自Effective
C++)

  附上正确代码: 


 1 #include <iostream>
2 #include <vector>
3 #include <algorithm>
4
5 struct finder
6 {
7 //永远让比较函数中对相等值返回false
8 bool operator()(int first, int second){return first < second;}
9 } my_finder;
10
11 int main(int argc, char** argv)
12 {
13 int value = atoi(argv[1]);
14 int num = atoi(argv[2]);
15 std::vector<int> vecTest;
16 for(int i=0; i!=num; ++i)
17 vecTest.push_back(value);
18
19 std::sort(vecTest.begin(), vecTest.end(), my_finder);
20 for(int i=0; i!=vecTest.size(); ++i)
21 std::cout<<vecTest[i]<<‘\t‘;
22 std::cout<<std::endl;
23
24 return 0;
25 }

结果:

欢迎转载交流,请注明出处:http://www.cnblogs.com/yuanzz/p/3735213.html

stl sort分析,布布扣,bubuko.com

时间: 2024-10-14 00:58:59

stl sort分析的相关文章

STL源代码分析——STL算法sort排序算法

前言 因为在前文的<STL算法剖析>中,源代码剖析许多,不方便学习,也不方便以后复习.这里把这些算法进行归类,对他们单独的源代码剖析进行解说.本文介绍的STL算法中的sort排序算法,SGI STL中的排序算法不是简单的高速排序,而是交叉利用各种排序:堆排序.插入排序和高速排序:这样做的目的是提高效率.针对数据量比較大的採用高速排序,数据量比較小的能够採用堆排序或插入排序. 本文介绍了有关排序的算法random_shuffle.partition.stable_partition.sort.s

STL sort

STL的sort()算法,数据量大时采用Quick Sort,分段递归排序,一旦分段后的数据量小于某个门槛,为避免Quick Sort的递归调用带来过大的额外负荷,就改用Insertion Sort.如果递归层次过深,还会改用Heap Sort.本文先分别介绍这个三个Sort,再整合分析STL sort算法(以上三种算法的综合) -- Introspective Sorting(内省式排序). 一.Insertion Sort Insertion Sort是<算法导论>一开始就讨论的算法.它的

shuffle和sort分析

MapReduce中的Shuffle和Sort分析 MapReduce 是现今一个非常流行的分布式计算框架,它被设计用于并行计算海量数据.第一个提出该技术框架的是Google 公司,而Google 的灵感则来自于函数式编程语言,如LISP,Scheme,ML 等.MapReduce 框架的核心步骤主要分两部分:Map 和Reduce.当你向MapReduce 框架提交一个计算作业时,它会首先把计算作业拆分成若干个Map 任务,然后分配到不同的节点上去执行,每一个Map 任务处理输入数据中的一部分

c++ STL sort struct comp

详细解说 STL 排序(Sort) http://www.cppblog.com/mzty/archive/2005/12/15/1770.html 详细解说 STL 排序(Sort) 作者Winter 详细解说 STL 排序(Sort) 0 前言: STL,为什么你必须掌握 1 STL提供的Sort 算法 1.1 所有sort算法介绍 1.2 sort 中的比较函数 1.3 sort 的稳定性 1.4 全排序 1.5 局部排序 1.6 nth_element 指定元素排序 1.7 partit

STL sort()函数

C++之所以得到这么多人的喜欢,是因为它既具有面向对象的概念,又保持了C语言高效的特点.STL 排序算法同样需要保持高效.因此,对于不同的需求,STL提供的不同的函数,不同的函数,实现的算法又不尽相同. 1.1 所有sort算法介绍 所有的sort算法的参数都需要输入一个范围,[begin, end).这里使用的迭代器(iterator)都需是随机迭代器(RadomAccessIterator), 也就是说可以随机访问的迭代器,如:it+n什么的.(partition 和stable_parti

STL sort 源码解析

前言 --本文整理自<STL源码解析> 虽然源码解析的代码比较老但是核心思想并没有太多变化并且直接看源码有太多细节我又看不懂最新的. 简介 sort接受两个RandomAccessIterators(随机存储迭代器),然后将区间内的所有元素以渐増的方式由小到大重新排列,第二个版本允许用户指定一个仿函数作为排序标准,STL所有关系型容器都拥有自动排序功能,不需要sort,stack,queue,priority-queue都有特别出入口,不允许排序,剩下vector,deque和list,前两者

vs2013 std::sort 分析

由于之前在debug模式下发现stl的sort简直慢到不能忍,所以自己写了一个sgi的sort,后来发现在release模式下,vs自带的sort快的不行,就研究了下. 这里有些和sgi-stl相通的东西就简略带过了,详细内容可以看我之前的stl源码的笔记: sgi-sort_link 首先来看下大概的过程: 1.没有调用到一定深度时,就进行划分并进行递归调用. 2.如果超过了一定深度时,这个区间改为调用堆排序.(这一部待商榷) 3.对剩下的小于32长度的区间进行插入排序. 接下来是详细分析:

nyoj 41-三个数从小到大排序(STL --&gt; sort(a, a+n) 升序)

41-三个数从小到大排序 内存限制:64MB 时间限制:3000ms Special Judge: No accepted:31 submit:44 题目描述: 现在要写一个程序,实现给三个数排序的功能 输入描述: 输入三个正整数 输出描述: 给输入的三个正整数排序 样例输入: 复制 20 7 33 样例输出: 7 20 33 分析: 直接使用STL库中的sort函数进行排序 C/C++代码实现(AC): #include <iostream> #include <algorithm&g

STL Sort Algorithm

这个星期看了侯捷先生<STL 源码剖析>算法部分,基本看完了,其中算法比较多,我就重点下Sort在SGI STL中的实现. 1. sort 函数的实现是这样的: template <class RandomAccessIterator> inline void sort(RandomIAccessIterator first , RandomAccessIterator last> { if ( first != last) { __introsort_loop(fisrt,