看标题,你可能会疑惑:咦?你这家伙,怎么不讲解完整的快排,只讲一部分快排……-。-
哎,冤枉。“部分快排”是算法的名字,实际上本文相当详细呢。本文几乎与普通快排无异。看懂了本文,你对普通的快排也会有更深的认识了。
快速排序算法(qsort)的原理我们大都应该了解。本文介绍的是部分快速排序算法。其实其算法本质是一样的,只不过限定了排序的左右区间,也就是只对一个数字序列的一部分进行排序,故称为“部分快速排序算法”,简称:
pqsort
Redis
项目中的pqsort.c
文件实现了pqsort()
函数,其源码见本文最后一节 pqsort.c源码 。 另外补充一句:长文慎入 :-)
导读
外部资料
维基百科
快排基本流程不了解的童鞋,请移步 快速排序wiki
论文
实际上pqsort.c
的快排流程是改编自一个经典实现,该实现被许多库的实现所使用。请参考Bentley & McIlroy
所著论文 “Engineering a Sort Function”
源码结构
主要函数 | pqsort() |
---|---|
静态函数 | _pqsort()、swapfuc()、med3() |
宏函数 | min()、swap()、swapcode()、vecswap()、SWAPINIT() |
总体来说,pqsort.c文件对外只提供了一个函数—— pqsort() ,但它的算法逻辑其实是由_pqsort()实现的,其他的静态(static)函数和宏也都是为了该函数服务的。
接下来的介绍中,我会简单介绍宏函数和几个静态函数,把重点放在静态函数_pqsort()上。它才是整个算法的核心部分。
pqsort()与qsort()
C标准库
中有一个快排的函数qsort()
,它与本文介绍的pqsort()
所提供的编程接口极为相似,请看两者声明:
void qsort (void *a, size_t n, size_t es, int (*cmp)(const void *, const void *));
void pqsort(void *a, size_t n, size_t es, int (*cmp)(const void *, const void *),
size_t lrange, size_t rrange);
参数解读
参数 | 说明 |
---|---|
a | 待排序数组的首地址 |
n | 待排序元素的个数 |
es | element size :每个元素的字节大小 |
cmp | 回调函数,定义了比较的规则,直接影响排序结果是递增排序或递减排序,并支持非标准类型的排序 |
lrange | 待排序的左边界 |
rrange | 待排序的右边界 |
pqsort()与_pqsort()
pqsort()源码
void
pqsort(void *a, size_t n, size_t es,
int (*cmp) (const void *, const void *), size_t lrange, size_t rrange)
{
_pqsort(a,n,es,cmp,((unsigned char*)a)+(lrange*es),
((unsigned char*)a)+((rrange+1)*es)-1);
}
可以看出我们的qpsort()其实是在调用_pqsort()来完成排序功能的。这两个函数很像,差别在于参数上。
看一下两者的函数原型:
void
pqsort (void *a, size_t n, size_t es, int (*cmp)(const void *, const void *),
size_t lrange, size_t rrange);
static void
_pqsort(void *a, size_t n, size_t es, int (*cmp)(const void *, const void *),
void *lrange, void *rrange)
差异的关键在于:
- pqsort() 的参数中的左右边界值,其含义值下标
- _pqsort()的参数中的左右边界值,其含义是指针
这样pqsort()源码就不足为奇了。所以我前面说该文件的核心部分是_pqsort()
预备知识
看一下除了_pqsort()之外的源码部分,这些都是_pqsort()函数实现的辅助。
med3
static inline char *
med3(char *a, char *b, char *c,
int (*cmp) (const void *, const void *))
{
return cmp(a, b) < 0 ?
(cmp(b, c) < 0 ? b : (cmp(a, c) < 0 ? c : a ))
:(cmp(b, c) > 0 ? b : (cmp(a, c) < 0 ? a : c ));
}
根据回调函数cmp
指定的比较规则,则求出变量a,b,c中处于中间大小的变量。换句话说:就是在求 中位数。
min
#define min(a, b) (a) < (b) ? a : b
这是个简单的宏,看一眼就呵呵就行了。