目录
一.qsort函数的使用
1.
1.qsort函数是用来排序任意数据类型的数组,对其中的元素进行一定规则的排列
2.qsort不返回任何值
3.qsort的第一个参数是一个 void* 指针,指向数组的第一个元素
4.qsort的第二个参数是size_t类型的参数,代表的是数组中元素的个数
5.qsort的第三个参数是size_t类型的参数,代表每个元素的大小,单位是字节,比如int类型就是4个字节,char类型就是1个字节
6.qsort的第四个参数是一个函数指针,指向一个返回值为int参数为两个 const void*的
函数,函数的参数分别指向两个元素,如果第一个元素大于第二个元素,就返回一个大于0的值,如果第一个元素小于第二个元素,就返回一个小于0的值,如果第一个元素等于第二个元素,就返回0,该函数用于比较数组中的元素,并且该函数需要我们自己创建。
7.使用qsort时需要包含头文件<stdlib.h>
示例
1.这里,我们想对于int类型的数据比较,将int类型的元素从小到大排列,我们创建了一个函数int_cmp来比较两个int类型的元素,先把两个const void*指针的参数强制类型转化成int*类型的参数,这样再解引用,一次读取四个字节,就可以得到这个int类型的值了,return的是两个int元素相减的值,如果第一个元素大于第二个数,那么返回值一定是大于0的,同理,如果第一个元素小于第二个元素,返回小于0的数,如果第一个元素等于第二个元素,返回0,符合qsort第四个参数的标准。
2.我们依次将参数(数组首元素地址,元素个数,元素大小,比较函数)输入到qosrt中,实现整形类型的排序
3.此函数不止能排列整形数组,可以排列任意类型的数组(整形数组,字符数组,结构体数组..)
只要能够写出判断的函数,就能够对数组进行排序,因为排序的规则是自己制定的,上述代码是对结构体数组按元素中age的从小到大进行排序的例子,年龄小从第二个元素被换到第一个元素。通过比较每个结构体中age的大小实现比较函数age_cmp。
二.使用冒泡排序模拟实现qsort函数
二.1.冒泡排序
1.什么是冒泡排序呢?简单来说就是通过比较数组中两两相邻的元素进行排序。
2.比如,现在有一个如图所示的数组,我们要将其从小到大依次排序,我们先比较第一个和第二个元素,如果第一个元素大于第二个元素,那么我们就将这两个元素的值交换,反正,则保持不变。
4小于8,所以我们不进行交换,那么再比较第二个元素的第三个元素
8大于3,那么把第二个元素和第三个元素的值互换,依次类推,第一轮比较之后,效果如图所示
那么,到底要进行几轮这样的比较才能够将其排列完成呢?
只需要比较 元素的个数-1轮,即可完全排列好,因为你排列第一轮,就会确定一个最大值在数组的最后,第二轮,会确定一个倒数第二大的数在数组的倒数第二个元素,依次类推,直达排列 元素的个数-1轮,第一个位置因为其它的位置已经都被确定过了,所以无需再比较。
那么,每一轮需要比较几次呢?
第一轮两两比较,需要比较元素的个数-1次,但是第二轮由于数组的最后一个元素已经被确定,不用比较数组的倒数第二个元素和倒数第一个元素,所以第二轮会比第一轮少比较一次。
假如数组有n个元素第i轮排列就需要比较n-i次。
现在,让我们用代码实现一个排列元素是int类型数组的冒泡排序。
二.2.模拟实现qsort函数
这是函数最终的样子,看起来十分复杂,但是我们可以将其拆解,让其变得简单。
首先,我们先写出一个冒泡排序的框架,排列num-1轮,排列num-1-i次
然后,我们就需要比较相邻两个元素的大小,但是,我们并不知道它是什么类型的元素,所以我们需要用void*指针来接收数组首元素的地址,再根据接收到的元素个数和元素大小进行判断。
void*指针不能直接使用,我们先将其强制转化成char*类型(char*类型每次读取一个字节,每次加减一个字节),这样,第j+1个元素的地址就是(char*)base + j * size,比如,第一个元素的地址就是(char*)base
将相邻两个元素通过比较函数比较,如果返回值大于0,就将两个元素交换
那么,如何交换两个元素呢,我们可以把元素分成一个一个字节,把相邻元素的每个字节都进行交换,那么,每次交换相邻元素,就需要交换size个字节,所以,我们可以通过一个循环来实现
这样,我们就利用冒泡排序成功模拟实现了qsort函数
示例