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剩余的元素在原有的数组中的位置不需要变化,如果拷贝到临时空间,那么最后也要拷贝回来,所以没有必要拷贝。最后再将临时空间的元素拷贝回原数组中。

Glibc 源码阅读 -- qsort函数

(2)如果每个元素的大小>32,那么先将元素中的元素的指针拷贝到临时空间,再调用msort_with_tmp进行归并排序,归并排序只对指针进行排序,(如果拷贝元素本身会耗时比较长),最后按照临时空间中排序后的元素,对原数组元素进行排序。

Glibc 源码阅读 -- qsort函数

(3)数组非常大的情况调用_quicksort快速排序。

首先使用三数取中法得到分位点mid,之后左右两边的元素都和分位点比较,对low,mid,high指向元素排序,之后在左边找到第一个大于分位点的元素,在右边找到第一个小于分为点的元素为止,交换他们的位置,直到left_ptr>right_ptr为止(遍历所有元素完成)。

当需要排序的区间的元素不多余4个时,退化为插入排序。

相关文章: