写在前面:本博客仅作记录学习之用,部分图片来自网络,如需引用请注明出处,同时如有侵犯您的权益,请联系删除!
基于分治(Divide and Conquer)思想。它的核心思想是选择一个基准元素,通过将数组分割成两个子数组,使得左边的子数组中的所有元素都小于或等于基准值,右边的子数组中的所有元素都大于基准值。然后递归地对这两个子数组进行排序,最终完成整个数组的排序。
具体步骤如下:
快速排序的关键在于如何选择基准元素和如何分割子数组
。通常情况下,选择一个合适的基准元素可以使得分割后的子数组尽可能平衡,以提高算法的效率。例如,可以采用三数取中的方法,在数组的起始、中间和结束位置分别取样,然后选择其中的中位数作为基准元素,以减少最坏情况的出现。
快速排序是一种原地排序算法,不需要额外的辅助空间。
// 返回分界线
int get_mid(vector<int>& nums, int left, int right){
int i = left, j = right;
while(i < j){
// 降序
while(i < j && nums[j] <= nums[left]) j--;
while(i < j && nums[i] >= nums[left]) i++;
// 升序
// while(i < j && nums[j] >= nums[left]) j--;
// while(i < j && nums[i] <= nums[left]) i++;
swap(nums[i], nums[j]);
}
swap(nums[i], nums[left]);
return i;
}
void quickSort(vector<int>& nums, int left, int right){
if(left > right) return;
int mid = get_mid(nums, left, right);
quickSort(nums, left, mid - 1);
quickSort(nums, mid + 1, right);
}
void quicksort(vector<int>& nums, const int& size){
stack<pair<int, int>> lr_stack;
lr_stack.push(make_pair(0, size));
while(!lr_stack.empty()){
auto tmp = lr_stack.top();
lr_stack.pop();
int mid = get_mid(nums, tmp.first, tmp.second);
if(tmp.first < mid - 1) lr_stack.push(make_pair(tmp.first, mid - 1));
if(mid + 1 < tmp.second) lr_stack.push(make_pair(mid + 1, tmp.second));
}
}
因此有必要了解和Sort的区别。 库函数 sort 和上述快排代码的区别主要在以下几个方面:
实现方式不同: sort 函数通常采用一种混合了多种排序算法的方式来实现数组的排序,如快速排序、归并排序、堆排序等等。具体采用哪种排序算法,取决于数组的大小和数据分布情况。
代码实现复杂度不同: sort 库函数的实现是高度优化的,如采用了分段递归、尾递归优化、使用小范围插入排序等等。
排序速度和效率不同: 由于 sort 库函数使用了多种排序算法的混合方式,同时采用了许多优化措施,在大多数情况下, sort 函数的速度和效率通常是要优于手写的快速排序算法的。
template<typename _RandomAccessIterator>
inline void
sort(_RandomAccessIterator __first, _RandomAccessIterator __last)
{
typedef typename iterator_traits<_RandomAccessIterator>::value_type
_ValueType;
// concept requirements
__glibcxx_function_requires(_Mutable_RandomAccessIteratorConcept<_RandomAccessIterator>)
__glibcxx_function_requires(_LessThanComparableConcept<_ValueType>)
__glibcxx_requires_valid_range(__first, __last);
if (__first != __last)
{
//快速排序+插入排序
std::__introsort_loop(__first, __last, std::__lg(__last - __first) * 2);
//插入排序
std::__final_insertion_sort(__first, __last);
}
}
以上代码是 C++ STL(标准模板库)中的 sort 函数模板实现。它用于对给定范围内的元素进行排序,使用了快速排序和插入排序两种算法。
函数的参数包括一个随机访问迭代器 __first
和 __last
,表示待排序范围的起始和结束位置。在函数内部,首先使用 typedef
定义了一个类型 _ValueType
,表示迭代器指向元素的值类型。接下来,使用一些宏定义进行了一些概念要求的检查,确保传入的迭代器满足要求,如可变的随机访问迭代器概念和可比较的值类型概念。然后,通过判断__first
和 __last
是否相等来确定待排序范围是否为空。如果不为空,则执行排序操作。
排序操作分为两个阶段:
快速排序 + 插入排序:调用 std::__introsort_loop
函数进行快速排序和插入排序的混合排序。其中,std::__lg(__last - __first) * 2
表示递归深度的限制,根据待排序范围的大小计算出最大递归深度。
插入排序:调用 std::__final_insertion_sort
函数对剩余的待排序范围(如果有的话)进行插入排序。
整体上,sort 函数模板实现了一种高效的排序算法,通过混合使用快速排序和插入排序,可以在大部分情况下获得较好的性能。同时,根据待排序范围的大小,动态调整递归深度,以避免快速排序的最坏时间复杂度,并在数据规模较小时使用插入排序进行优化。
template<typename _RandomAccessIterator, typename _Size>
void
__introsort_loop(_RandomAccessIterator __first,
_RandomAccessIterator __last,
_Size __depth_limit)
{
typedef typename iterator_traits<_RandomAccessIterator>::value_type
_ValueType;
//_S_threshold=16,每个区间必须大于16才递归
while (__last - __first > int(_S_threshold))
{
//达到指定递归深度,改用堆排序
if (__depth_limit == 0)
{
std::partial_sort(__first, __last, __last);
return;
}
--__depth_limit;
_RandomAccessIterator __cut =
std::__unguarded_partition(__first, __last,
_ValueType(std::__median(*__first,
*(__first
+ (__last
- __first)
/ 2),
*(__last
- 1))));
std::__introsort_loop(__cut, __last, __depth_limit);
__last = __cut;
}
}
上述代码__introsort_loop
函数采用了一种混合的排序算法,结合了快速排序、插入排序和堆排序,并通过递归和迭代的方式来进行排序。函数的命名为 __introsort_loop
,参数包括一个随机访问迭代器 __first
和 __last
,表示待排序范围的起始和结束位置,以及一个整数__depth_limit
,表示递归深度的限制。在函数内部,首先根据迭代器的类型,定义了一个 _ValueType
类型,它表示迭代器指向元素的值类型。然后,在一个循环中,判断待排序范围的大小是否大于一个阈值 _S_threshold
(通常是 16)。如果不满足该条件,则将排序范围分为两部分,并对其中一部分进行递归排序,另一部分则留待下一次迭代处理。当递归深度达到限制时,会使用 std::partial_sort
函数对剩余的待排序范围进行排序,该函数会使用堆排序算法来进行排序操作。
整体上,通过快速排序和插入排序来处理较大的待排序范围,而使用堆排序来处理递归深度达到限制的情况,以保证排序的效率和性能。这种混合算法的设计可以根据不同大小的输入数据自适应地选择最优的排序方式,以达到较好的排序效果。
欲尽善本文,因所视短浅,怎奈所书皆是瞽言蒭议。行文至此,诚向予助与余者致以谢意。