glibc-2.29
函数定义 msort.c
void
qsort (void *b, size_t n, size_t s, __compar_fn_t cmp)
{
return __qsort_r (b, n, s, (__compar_d_fn_t) cmp, NULL);
}
libc_hidden_def (qsort)
void *b 表示指向数组的指针
size_t n 表示数组中元素个数
size_t s 表示数组中每个元素的大小
__compar_fn_t cmp 表示比较函数,在stdlib.h中有定义,
#ifndef __COMPAR_FN_T
# define __COMPAR_FN_T
typedef int (*__compar_fn_t) (const void *, const void *);
__qsort_r 流程
void __qsort_r (void *b, size_t n, size_t s, __compar_d_fn_t cmp, void *arg)
1.这个算法是需要辅助内存空间的
(1)先计算出来需要开辟的内存空间大小。
如果每个元素的大小<=32,那么size=n*s;
如果每个元素的大小>32,那么size=2 * n * sizeof (void *) + s;
(2)如果size<1024,那么在栈上开辟空间。
struct msort_param
{
size_t s;
size_t var;
__compar_d_fn_t cmp;
void *arg;
char *t;
};
struct msort_param p;
p.t 指向开辟的空间
p.s = s; // 每个元素的大小
p.var = 4; // ?
p.cmp = cmp; // 比较函数
p.arg = arg; // NULL ?
当size>=1024时,如果数组占的物理页面数(size/pagesize)大于总物理页面数的1/4, 或是在堆上开辟空间(malloc)失败时,那么调用_quicksort函数。其他情况在堆上开辟空间。
2.开始排序
(1)如果每个元素的大小<=32,那么直接调用msort_with_tmp进行归并排序。
归并分为分解和合并两部分,分解就利用递归的方法,直到元素不可再分割(小于等于1个元素)。
合并就是对分解出来的数组进行合并。如下图所示对b1和b2所指向的数组进行排序,将比较出来较小的元素先放入临时开辟的空间(p.t)中,当两个数组比较到最后时,可能b1有剩余元素,或是b2有剩余元素。如果b1有剩余元素,那么需要将b1剩余的元素也拷贝到临时空间,如果b2有剩余元素那么就不需要了(重要:这里节省了一些拷贝),因为如果把b2剩余的元素在原有的数组中的位置不需要变化,如果拷贝到临时空间,那么最后也要拷贝回来,所以没有必要拷贝。最后再将临时空间的元素拷贝回原数组中。
(2)如果每个元素的大小>32,那么先将元素中的元素的指针拷贝到临时空间,再调用msort_with_tmp进行归并排序,归并排序只对指针进行排序,(如果拷贝元素本身会耗时比较长),最后按照临时空间中排序后的元素,对原数组元素进行排序。
(3)数组非常大的情况调用_quicksort快速排序。
首先使用三数取中法得到分位点mid,之后左右两边的元素都和分位点比较,对low,mid,high指向元素排序,之后在左边找到第一个大于分位点的元素,在右边找到第一个小于分为点的元素为止,交换他们的位置,直到left_ptr>right_ptr为止(遍历所有元素完成)。
当需要排序的区间的元素不多余4个时,退化为插入排序。