跟我学c++中级篇——STL算法之排序
Posted 太平洋工作室
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了跟我学c++中级篇——STL算法之排序相关的知识,希望对你有一定的参考价值。
一、排序算法
数据结构和算法里,或者说在计算机的算法里,排序算法一定是无法绕过的一个算法,而且排序算法种类很多,和树、堆等数据结构都多多少少的互相纠缠。常见的排序算法按不同的特点可以分为比较类型排序和非比较类型排序,内部排序和外部排序等,包括以下几种:
冒泡排序算法、选择排序算法、插入排序算法、快速排序算法、希尔排序算法、堆排序算法、归并排序算法。其它如基数排序算法、桶排序算法和计数排序算法,不是很常见。
其主要的特征如下:
排序是基本的算法,在各种语言和库中都或多或少的有封装,在STL中当然也有对排序的封装,一般常用的是选择排序和快速排序以及堆排序比较多。在STL也可以通过提供自定义的排序算法进行排序处理。
二、STL库中的排序
STL的排序整体是以快速为主(分段递归),但当分段排序时达到某个域值,就会使用插入排序,当然递归层次过深,则采用堆排序,防止出现最坏的情况。换句话说,在STL库的排序中,是根据情况动态的使用三种排序方法来达到最优的排序速度的。STL中的比较主要分为有以下几种:
1、整体的比较,如sort()函数。
2、区间比较,如partial_sort()函数。
3、堆排序函数,sort_heap()函数。
在c++20中还有一个std::ranges::sort()函数,这个看起来更好用更方便一些。
看一下相关的源码:
template<class _RanIt,
class _Pr> inline
void sort(const _RanIt _First, const _RanIt _Last, _Pr _Pred)
{ // order [_First, _Last), using _Pred
_Adl_verify_range(_First, _Last);
const auto _UFirst = _Get_unwrapped(_First);
const auto _ULast = _Get_unwrapped(_Last);
_Sort_unchecked(_UFirst, _ULast, _ULast - _UFirst, _Pass_fn(_Pred));
}
template<class _RanIt,
class _Pr> inline
void _Sort_unchecked(_RanIt _First, _RanIt _Last, _Iter_diff_t<_RanIt> _Ideal, _Pr _Pred)
{ // order [_First, _Last), using _Pred
_Iter_diff_t<_RanIt> _Count;
while (_ISORT_MAX < (_Count = _Last - _First) && 0 < _Ideal)
{ // divide and conquer by quicksort
auto _Mid = _Partition_by_median_guess_unchecked(_First, _Last, _Pred);
// TRANSITION, VSO#433486
_Ideal = (_Ideal >> 1) + (_Ideal >> 2); // allow 1.5 log2(N) divisions
if (_Mid.first - _First < _Last - _Mid.second)
{ // loop on second half
_Sort_unchecked(_First, _Mid.first, _Ideal, _Pred);
_First = _Mid.second;
}
else
{ // loop on first half
_Sort_unchecked(_Mid.second, _Last, _Ideal, _Pred);
_Last = _Mid.first;
}
}
if (_ISORT_MAX < _Count)
{ // heap sort if too many divisions
_Make_heap_unchecked(_First, _Last, _Pred);
_Sort_heap_unchecked(_First, _Last, _Pred);
}
else if (2 <= _Count)
{
_Insertion_sort_unchecked(_First, _Last, _Pred); // small
}
}
template<class _BidIt,
class _Pr> inline
_BidIt _Insertion_sort_unchecked(_BidIt _First, const _BidIt _Last, _Pr _Pred)
{ // insertion sort [_First, _Last), using _Pred
if (_First != _Last)
{
for (_BidIt _Next = _First; ++_Next != _Last; )
{ // order next element
_BidIt _Next1 = _Next;
_Iter_value_t<_BidIt> _Val = _STD move(*_Next);
if (_DEBUG_LT_PRED(_Pred, _Val, *_First))
{ // found new earliest element, move to front
_Move_backward_unchecked(_First, _Next, ++_Next1);
*_First = _STD move(_Val);
}
else
{ // look for insertion point after first
for (_BidIt _First1 = _Next1;
_DEBUG_LT_PRED(_Pred, _Val, *--_First1);
_Next1 = _First1)
{
*_Next1 = _STD move(*_First1); // move hole down
}
*_Next1 = _STD move(_Val); // insert element in hole
}
}
}
return (_Last);
}
可以很明显的在代码中看到判断来使用插入和堆排序算法。其它的几个排序基本原理是相似的,不再阐述分析。
三、例程
应用例程就很简单了(cppreference.com例程):
#include <algorithm>
#include <functional>
#include <array>
#include <iostream>
#include <string_view>
int main()
{
std::array<int, 10> s = {5, 7, 4, 2, 8, 6, 1, 9, 0, 3};
auto print = [&s](std::string_view const rem) {
for (auto a : s) {
std::cout << a << ' ';
}
std::cout << ": " << rem << '\n';
};
std::sort(s.begin(), s.end());
print("sorted with the default operator<");
std::sort(s.begin(), s.end(), std::greater<int>());
print("sorted with the standard library compare function object");
struct {
bool operator()(int a, int b) const { return a < b; }
} customLess;
std::sort(s.begin(), s.end(), customLess);
print("sorted with a custom function object");
std::sort(s.begin(), s.end(), [](int a, int b) {
return a > b;
});
print("sorted with a lambda expression");
}
运行结果如下:
0 1 2 3 4 5 6 7 8 9 : sorted with the default operator<
9 8 7 6 5 4 3 2 1 0 : sorted with the standard library compare function object
0 1 2 3 4 5 6 7 8 9 : sorted with a custom function object
9 8 7 6 5 4 3 2 1 0 : sorted with a lambda expression
partial_sort是对指定的位置和范围的部分数据进行排序,如果这个函数能满足你的需求,就尽量不要使用全排序。堆排序sort_heap()和书本上的没啥本质不同,同样看代码:
#include <algorithm>
#include <vector>
#include <iostream>
int main()
{
std::vector<int> v = {3, 1, 4, 1, 5, 9};
std::make_heap(v.begin(), v.end());
std::cout << "heap:\t";
for (const auto &i : v) {
std::cout << i << ' ';
}
std::sort_heap(v.begin(), v.end());
std::cout << "\nsorted:\t";
for (const auto &i : v) {
std::cout << i << ' ';
}
std::cout << '\n';
}
执行结果是:
heap: 9 4 5 1 1 3
sorted: 1 1 3 4 5 9
库存在的目的就是减少使用的麻烦,不过减少使用的麻烦不代表没坑,如果排序数据中有重复的数据结果会是如何呢?同学们可以自己试试,再结合网上的说明资料就明白原因了。
四、总结
知其然,然后知其所然,是知也。很多人编程很多年,也不知道上学时学过的数据结构和算法用处在哪儿。当你透过应用的层层封装的迷雾之后,就会发现,原来自己只是一只“勤劳”的工蜂,不断的用别人提供的工具在干着一些简单而又重复的劳动,为什么不自己去做一些有意义的事情呢?适当的重复的搞一两个轮子,未必不是好事。
以上是关于跟我学c++中级篇——STL算法之排序的主要内容,如果未能解决你的问题,请参考以下文章