【问题标题】:Which type of sorting is used in the std::sort()?std::sort() 中使用哪种类型的排序?
【发布时间】:2009-12-03 14:17:20
【问题描述】:

谁能告诉我<algorithm> 头文件中定义的std::sort() 函数中实现了哪种类型的排序技术(冒泡、插入、选择、快速、合并、计数...)?

【问题讨论】:

  • 不是你的问题,但它在这里说:cplusplus.com/reference/algorithm/sort 平均为 nlogn,最坏的情况为 n^2(快速排序也是如此)。
  • MSVC 帮助还指出“排序复杂度的平均值为 O(N log N),其中 N = _Last – _First。”
  • BTW——c 标准库函数sort() 的答案是一样的:在O(N log N) 运行的东西。有时,该手册页会告诉您系统实际使用的是什么。

标签: c++ sorting stl


【解决方案1】:

std::sort 的大多数实现都使用快速排序(或者通常是一种混合算法,如 introsort,它结合了快速排序、堆排序和插入排序)。

标准要求的唯一事情是std::sort 以某种方式根据指定的顺序对数据进行排序,复杂度约为 O(N log(N));不保证稳定。从技术上讲,introsort 比快速排序更能满足复杂性要求,因为快速排序具有二次最坏情况时间。

【讨论】:

  • 你有std::sort的快速排序实现吗?我见过的都使用原始 STL 中的 introsort。根据 C++11,O(n lg n) 要求不再是“近似的”。
  • 另外(至少在 Visual Studio 中)似乎对小范围数字执行计数排序进行了优化。我还没有阅读实现,但我知道排序是线性的,超过一百万个数字,范围约为 100 万。
  • 我从未见过不使用 introsort 的实现。您应该支持“大多数实现......”声明或修改您的答案。
  • 我在下面给出了类似的答案,并提供了有关 introsort 的更多详细信息。另外,我认为 introsort 从一开始就是 std::sort 的“标准 STL 实现”(而不是快速排序)。
【解决方案2】:

C++ 标准 ISO/IEC 14882:2003

25.3.1.1 排序

template<class RandomAccessIterator>
   void sort(RandomAccessIterator first, RandomAccessIterator last);
template<class RandomAccessIterator, class Compare>
   void sort(RandomAccessIterator first, RandomAccessIterator last,
          Compare comp);

1 效果:对元素中的元素进行排序 范围[第一个,最后一个)。

2 复杂性: 大约 N log N(其中 N == 最后 - 首先)平均比较。

没有关于方法的信息,但复杂度总是N log N

【讨论】:

  • 您的文字和报价不匹配。 N log N 是平均的,但它并没有说明最坏的情况。
【解决方案3】:

MSVC2013 STL中用到了三种算法,参考std::sort的源码。

最有可能使用QuickSort,或QuickSort 的变体IntroSort

如果递归太深,这里将使用HeapSort

否则将使用InsertSort

【讨论】:

    【解决方案4】:

    std::sort 的所有实现可能都使用 introsort(又名 introspection sort),这是一种结合了快速排序和堆排序的混合算法。实际上,introsort 是在 1997 年特别发明的,目的是在 C++ STL 中实现高性能排序。

    该标准唯一要求的是std::sort以某种方式根据指定的顺序对数据进行排序,复杂度为O(N log(N));它不能保证是稳定的(如果需要,可以使用单独的std::stable_sort 算法)。

    从技术上讲,introsort 比快速排序更能满足复杂度要求:这是因为堆排序在最坏情况下保证了 O(N log(N)) 复杂度,而快速排序具有二次最坏情况时间。

    但是,在一般情况下,堆排序比快速排序“慢”,因为堆排序执行 C*N log(N) 而快速排序执行 D*N log(n) 性能,D 明显小于 C(数字 CD 是常数) .也就是说,heapsort 的 per-comparison-overhead 比 quicksort 的要高。

    为了两全其美,introsort 从快速排序(一种递归算法)开始,但是当递归深度变得太高时(这意味着它会陷入退化最坏情况的行为),它切换到堆排序。

    另请参阅 David Musser 的 the Wikipedia entry for introsortoriginal paper,他专门为 STL 发明了 introsort。

    【讨论】:

    【解决方案5】:

    GCC 9.2.0 libstdc++ 源码确认 introsort

    其他 have mentioned introsort,但这里有一些 libstc++ 的证据,它是大多数 Linux 发行版上的默认 C++ 实现。

    libstdc++ 是 GCC 本身的一部分,因此我们将研究 GCC 源代码。

    libstdc++-v3/include/std/algorithm 是主标题,包含:

    #include <bits/stl_algobase.h>
    #include <bits/stl_algo.h>
    

    然后,bits/stl_algo.h 包含排序重载的定义,其中之一是:

      /**
       *  @brief Sort the elements of a sequence.
       *  @ingroup sorting_algorithms
       *  @param  __first   An iterator.
       *  @param  __last    Another iterator.
       *  @return  Nothing.
       *
       *  Sorts the elements in the range @p [__first,__last) in ascending order,
       *  such that for each iterator @e i in the range @p [__first,__last-1),  
       *  *(i+1)<*i is false.
       *
       *  The relative ordering of equivalent elements is not preserved, use
       *  @p stable_sort() if this is needed.
      */
      template<typename _RandomAccessIterator>
        inline void
        sort(_RandomAccessIterator __first, _RandomAccessIterator __last)
        {
          // concept requirements
          __glibcxx_function_requires(_Mutable_RandomAccessIteratorConcept<
            _RandomAccessIterator>)
          __glibcxx_function_requires(_LessThanComparableConcept<
            typename iterator_traits<_RandomAccessIterator>::value_type>)
          __glibcxx_requires_valid_range(__first, __last);
          __glibcxx_requires_irreflexive(__first, __last);
    
          std::__sort(__first, __last, __gnu_cxx::__ops::__iter_less_iter());
        }
    

    所以我们看到这只是对输入进行了一系列健全性检查,然后调用std::__sort,即defined in the same file

      template<typename _RandomAccessIterator, typename _Compare>
        inline void
        __sort(_RandomAccessIterator __first, _RandomAccessIterator __last,
           _Compare __comp)
        {
          if (__first != __last)
        {
          std::__introsort_loop(__first, __last,
                    std::__lg(__last - __first) * 2,
                    __comp);
          std::__final_insertion_sort(__first, __last, __comp);
        }
        }
    

    我会在这里停下来,我们已经达到了一个名为 std::__introsort_loop 的标识符,其余的实现都在同一个文件上,如果有人仍然有疑问的话。

    std::set 中提到的std::set 中提到的What is the underlying data structure of a STL set in C++?

    C++17 并行排序

    我们现在也有并行排序,所以也值得看看它是如何完成的:Are C++17 Parallel Algorithms implemented already?

    如上面回答中提到的,实现依赖于一个外部库:Intel Thread Building Blocks:https://github.com/intel/tbb

    【讨论】:

      【解决方案6】:

      你的意思是 std::sort 吗?如果是这样,它可以以他们想要的任何方式实施。它可能是快速排序,但可能是基数或其他东西。只要它在至少 O(n log n) 内为您生成一个排序列表,实现就很好,afaik。

      【讨论】:

      • 还有一个复杂度要求,即算法为 O(n log n)。这不是一个很大的限制,因为几乎所有常见的类型都满足这个要求。确实会阻止使用 Bogo 排序 (en.wikipedia.org/wiki/Bogo_sort)。
      • 我认为基数排序 isn't 是允许的,(即使它可能对某些数据类型有效)因为它没有对数复杂性,因为它不是基数比较。
      • 哦对吗?那么,如果它执行 O(1) 也会违反规则吗? (忽略这种算法的不可能性)。
      • @Goz 不,O(1) 实现就可以了。要求是复杂性的上限。它们通常设置为最有效的已知实现,但不排除一些更有效的新数据结构或算法。
      • std::sort 是基于比较的,它本质上阻止了比 O(n log n) 更快的实现。
      【解决方案7】:

      只是一些实证结果:

      我使用 std::sort(VS2008 工具链)将一个使用 numpy 1.9.2 排序的 Python 脚本翻译成 C++。

      当我使用 numpy.sort 参数 kind='mergesort' 时,我只能在 python 和 C++ 方面得到完全相同的结果。当 kind='quicksort' 或 kind='heapsort' 时,对于具有相同键的元素,我得到不同的相对排序。所以我猜至少对于VS2008附带的STL版本std::sort使用了mergesort。

      【讨论】:

        猜你喜欢
        • 2010-12-22
        • 2010-12-15
        • 1970-01-01
        • 2012-05-26
        • 1970-01-01
        • 1970-01-01
        • 2015-10-27
        • 1970-01-01
        • 2010-12-11
        相关资源
        最近更新 更多