C++STL 快速排序算法(sort)的原理(源码解析)

本文分析了C++标准库中的sort函数源码,主要涉及__sort的实现,包括快速排序、混合策略(当递归深度达到阈值时切换到堆排序,小范围用插入排序)以及__introsort_loop和__final_insertion_sort的具体功能。

分析下sort源码

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。排序的顺序由 __gnu_cxx::__ops::__iter_less_iter() 指定。

下面分析std::__sort

__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);
	}
    }

这个函数的主要逻辑是:

  1. 检查要排序的范围是否为空,如果为空则无需进行排序。
  2. 使用 std::__introsort_loop 函数进行快速排序。这是一种混合排序算法,首先使用快速排序,但在递归深度超过一定阈值时,切换到堆排序。
  3. 使用 std::__final_insertion_sort 函数进行最终的插入排序。插入排序对小型范围的元素进行高效排序。

这个混合排序的策略可以在大多数情况下保持较好的性能,因为快速排序在大部分情况下表现良好,而插入排序适用于小规模的数据。

接下来分别查看:__introsort_loop 和__final_insertion_sort 

__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;
	}
    }
  1. 使用 while 循环迭代排序范围,直到范围的大小小于 _S_threshold(一个阈值,通常是快速排序切换到插入排序的阈值)。
  2. 如果递归深度达到了预定的深度限制(__depth_limit == 0),则切换到堆排序。这是为了避免快速排序的最坏情况下的性能问题。
  3. 否则,使用 std::__unguarded_partition_pivot 函数分割数组,并对分割后的子数组进行递归快速排序。然后更新排序范围 __last
  4. 循环直到排序范围的大小小于 _S_threshold

这段代码的作用是执行快速排序的主要循环部分,通过分割数组并递归地对子数组进行排序来完成整个排序过程。

__final_insertion_sort(_RandomAccessIterator __first,_RandomAccessIterator __last, _Compare __comp)
    {
      if (__last - __first > int(_S_threshold))
	{
	  std::__insertion_sort(__first, __first + int(_S_threshold), __comp);
	  std::__unguarded_insertion_sort(__first + int(_S_threshold), __last,__comp);
	}
      else
	std::__insertion_sort(__first, __last, __comp);
    }
  1. 检查排序范围大小:首先,算法检查要排序的元素数量是否大于预设的阈值_S_threshold。这个阈值是基于性能考量设定的,目的是在较小的数据集上使用插入排序,而在较大的数据集上采用更复杂的排序算法。

  2. 条件分支

    1. 如果范围内的元素数量不大于阈值,则直接对整个范围[__first, __last)使用std::__insertion_sort进行插入排序.

    2. 如果范围内的元素数量大于阈值,则执行两步操作:

      1. 局部插入排序:使用std::__insertion_sort对范围[__first, __first + _S_threshold)内的元素进行插入排序。这意味着只对开始的部分元素进行排序,以快速地将这部分数据整理成有序状态。

      2. 无保护插入排序:随后,对剩余的元素(即[__first + _S_threshold, __last)范围内的元素)使用std::__unguarded_insertion_sort进行插入排序。这个"无保护"版本的插入排序假设前面的元素已经被排序,因此可以省略某些边界检查,从而提高效率。

综合来说,sort 算法的实现细节如下:

  • 初始时使用快速排序。
  • 当递归深度达到阈值时切换到堆排序。
  • 当待排序序列的长度变得较小时切换到插入排序。
  • 根据需要排序的元素数量是否大于阈值,决定用那种插入排序或者混合。
这种混合排序策略能够结合各种排序算法的优点,以在不同情况下实现高效的排序
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值