引言
很久没写快速排序了,没事的时候尝试着回忆了一下,顺便结合泛型编程的思想,将代码改成了void * ,满足所有数据类型的要求。
基本思想
快速排序最大的问题就是在不额外赋值空间的情况下,怎么做到在不变的空间内,按照快排的思想,将base基点左侧都放上比它小的值,base基点右侧都放上比它大的值。
很多时候base基点直接选择的是第一个元素,也是为了方便我们的排序。
但为了防止特殊情况出现,导致快排效率不高,base基点的选择最好是随机的,但只要将随机选中的base和第一个位置的元素交换位置就可以同样达到我们想要的效果。所以思想的讲解主要从base基点作为第一个元素来讲解。
交换策略
为了时刻保证base基点左侧都放上比它小的值,base基点右侧都放上比它大的值。建立左右两个指针l, r,保证始终 l < base , r > base。那么在一轮迭代过程中,先从右侧开始找到第一个小于base位置上的数进行交换,否则r-1。那么交换过去之后就以同样的思想跟左侧的元素进行比较。
这样就能始终满足[l,r]的区间不断缩小,且保证了非[l,r]区间范围内的点都是满足要求的,且base总是处于[l,r]的边界上。
那么代码就很容易实现了,我先实现了个简单地对整数排序的方法:
|
|
泛型编程
但是很明显这个代码只能对整数有用,那我之后出现了字符串,甚至是自定义的结构体之类的该怎么办呢,怎么把通用性改成和algorithm包中的sort函数一样优秀呢。那么就需要引入void * 参数,以及函数指针。
void 在c++代表一个可以替代任何对象的指针,类似java中的Object,有点所有对象的父类的感觉。所以void 作为参数,那么这个函数就能传入任何参数。
然后引入函数指针,对于排序来说,最重要的是比较两两的大小,那么就引入comparator函数指针来对对象进行比较。对于不同的对象可以有不同的比较方式,所以对不同的对象排序,我们只需要扩展写一份简单的比较函数,而不用在实现整个排序了。
|
|
因为void 的特殊性,对于void a来说,你可以通过a来获取这个位置的地址,但你不能通过 a + data_len 来得到下一个位置的地址, 而必须转化成具体数据类型的指针才能这样取址,需要注意的是比如 int b , b+1,其实是在b的地址上加了4个字节,因为一个int 32位4个字节,double b b+1,其实是在b的地址上加了8个字节,因为一个double 64位8个字节,转化成不同的类型都是不一样的。
我在此推荐的是转化成 char ,因为一个char就一个字节。正好和data_len的长度一一对应,用int ,还需要 除以4。
然后交换值的swap函数因为都是void *,是直接对内存地址进行操作,所以要用memcpy来进行赋值。
|
|
将上述的快速排序函数修改后就是
|
|
我在此定义了两种数据,整数和字符串进行排序。
|
|
最后main 函数实现就是
|
|
自定义数据类型排序
可能这样还不够体现出这样编程的好处。那么现在我自定义一个数据结构进行排序。最后写一份总结性的代码
|
|