From c08ae834a84d36d7e85ce5517a0fe354daee516b Mon Sep 17 00:00:00 2001 From: leafee98 Date: Sat, 29 Jul 2023 16:01:01 +0800 Subject: [PATCH] new essays: cpp-sort-implement --- content/essays/cpp-sort-implement.md | 114 +++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 content/essays/cpp-sort-implement.md diff --git a/content/essays/cpp-sort-implement.md b/content/essays/cpp-sort-implement.md new file mode 100644 index 0000000..7b2246d --- /dev/null +++ b/content/essays/cpp-sort-implement.md @@ -0,0 +1,114 @@ +--- +title: "C++ 的 sort 实现" +date: 2023-07-29T15:09:46+08:00 +tags: [] +categories: [] +weight: 50 +show_comments: true +draft: false +--- + +目前 C++ 中 std::sort 的实现使用的是 [内省排序 (Introsort)][1]、和插入排序共用的方式。当总元素数量小于一个阈值时(通常是 16),那么 sort 将只会使用插入排序。在内省排序中,当到达一定深度时(深度为元素数量的对数值),排序将转为堆排序。 + + + +```cpp + /// This is a helper function... + template + _GLIBCXX20_CONSTEXPR + inline _RandomAccessIterator + __unguarded_partition_pivot(_RandomAccessIterator __first, + _RandomAccessIterator __last, _Compare __comp) + { + _RandomAccessIterator __mid = __first + (__last - __first) / 2; + std::__move_median_to_first(__first, __first + 1, __mid, __last - 1, + __comp); + return std::__unguarded_partition(__first + 1, __last, __first, __comp); + } + + template + _GLIBCXX20_CONSTEXPR + inline void + __partial_sort(_RandomAccessIterator __first, + _RandomAccessIterator __middle, + _RandomAccessIterator __last, + _Compare __comp) + { + std::__heap_select(__first, __middle, __last, __comp); + std::__sort_heap(__first, __middle, __comp); + } + + /// This is a helper function for the sort routine. + template + _GLIBCXX20_CONSTEXPR + void + __introsort_loop(_RandomAccessIterator __first, + _RandomAccessIterator __last, + _Size __depth_limit, _Compare __comp) + { + while (__last - __first > int(_S_threshold)) + { + if (__depth_limit == 0) + { + std::__partial_sort(__first, __last, __last, __comp); + return; + } + --__depth_limit; + _RandomAccessIterator __cut = + std::__unguarded_partition_pivot(__first, __last, __comp); + std::__introsort_loop(__cut, __last, __depth_limit, __comp); + __last = __cut; + } + } + + template + _GLIBCXX20_CONSTEXPR + 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); + } + } + + /** + * @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 `[__first, __last)` in ascending order, + * such that for each iterator `i` in the range `[__first, __last - 1)`, + * `*(i+1) < *i` is false. + * + * The relative ordering of equivalent elements is not preserved, use + * `stable_sort()` if this is needed. + */ + template + _GLIBCXX20_CONSTEXPR + 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` 是下面第一个函数,它经过一些断言以后直接使用了 `std::__sort`,也就是倒数第二个函数。在 `std::__sort` 中,当容器内部不为空的时候,固定先使用内省排序再使用插入排序。在内省排序(`std::__introsort_loop`)中,若容器元素数量小于阈值就什么也不做,就相当于在数量较小时只使用插入排序,当达到深度限制以后,将转为堆排序(即 `std::__partial_sort` 的实现),否则(`std::__unguarded_partition_pivot`)将选取处于容器中间位置的元素作为基准,将小于基准的移动到左侧,大于基准的移动到右侧,并递归调用,即快速排序的思路。 + +关于深度限制为 2 倍的容器元素个数的对数,在理想情况下容器每次被二等分时是永远到达不了这个限制的,但需要注意基准的选择是直接选取位于中间位置的元素,也因此极大概率是无法二等分的,此时就能够达到容器深度的限制了。 + +[1]: https://zh.wikipedia.org/wiki/%E5%86%85%E7%9C%81%E6%8E%92%E5%BA%8F