一、qsort函数
1.1 qsort函数参数及返回类型
qsort执行一个快速排序,它是一个库函数,需要的头文件是<stdlib.h>。
功能是可以将任意类型数据按照升序排列。
通过上面函数的用法介绍,了解qsort无返回类型且有四个参数,我们一起来看下这个参数的含义吧!
base是指向目标待排序数据的第一个元素,它是一个无类型指针,可以接受任何类型地址,因为无类型,所以无法进行进行解引用操作。
num是目标待排列数据元素个数,类型是无符号整型。
width为目标待排序数据中每个元素的大小,以字节为单位,类型是无符号整型。
compare是一个函数指针,指向的该函数有比较俩个元素大小的功能。
这是compare指向函数的俩个参数,为被比较数据中俩个元素的地址,函数指针以无类型指针接受这俩个参数。
compare指向的函数返回类型为int,返回值分为三种情况:
1>elem1<elem2,返回一个小于0的值。
2>elem1=elem2,返回0。
3>elem1>elem2,返回一个大于0的值。
值得注意的是:compare指向的函数需要我们自己根据上面的返回值情况自己创建。
1.2 qsort函数的用法
下面我们先使用一下qsort函数,熟悉用法:
int compare(const void* e1, const void* e2) { return *(int*)e1 - *(int*)e2; } int main() { int i = 0; int arr[10] = { 2,1,5,4,3,9,8,7,6,0 }; int sz = sizeof(arr) / sizeof(arr[0]); qsort(arr, sz, sizeof(arr[0]), compare); for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++) { printf("%d ", arr[i]); } printf("\n"); return 0; }
这是将整型数组按升序排列,那结构体数组又该怎么用呢?
还是大同小异,注意函数参数的返回值与要求一致
struct stu { char name[20]; int score; }; int compare(const void* e1, const void* e2) { return((struct stu*)e1)->score - ((struct stu*)e2)->score; } int main() { int i = 0; struct stu s[3] = { {"zhangsan",140}, {"lisi",125}, {"wangwu",145}}; int sz = sizeof(s) / sizeof(s[0]); qsort(s, sz, sizeof(s[0]), compare); return 0; }
举了这俩个例子,想必大家对qsort的用法很熟悉了吧。
二、qsort函数的模拟实现
qsort函数是对任意类型数据进行升序排序,我们很容易想到冒泡排序,冒泡排序是对整型数据进行排序,冒泡排序相比大家都非常熟悉,我就不过多介绍,直接上代码,比较和qsort的区别:
#include <stdio.h> void sort(int arr[], int sz) { int i = 0; for (i = 0; i < sz - 1; i++)//冒泡趟数 { int j = 0; for (j = 0; j < sz - 1 - i; j++)//一趟的次数 { if (arr[j] > arr[j + 1]) { int tmp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = tmp; } } } } int main() { int i = 0; int arr[10] = { 2,1,5,4,3,9,8,7,6,0 }; int sz = sizeof(arr) / sizeof(arr[0]); sort(arr, sz); for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++) { printf("%d ", arr[i]); } printf("\n"); return 0; }
我们不难发现,冒泡排序(1)只适用于整型数据,(2)而且交换方式也是整型比较方式,不能通用其他类型,(3)所传参数少于qsort函数。
排序的趟数以及每一次排序次数都是同样的,所以我们修改的地方有:
(1)参数-----------------------------------------与qsort参数类似,可接受任意类型数据
(2)比较相邻俩元素大小方法---------------------不仅仅比较整型数据,可以比较任意类型数据
(3)交换位置方法-------------------------------不仅仅交换整型元素,可以交换任意元素
2.1 模拟qsort参数
#include <stdio.h> #include<stdlib.h> void bubble_sort(void *base, size_t sz, size_t width, int(* compare)(const void* e1, const void* e2)) { int i = 0; for (i = 0; i < sz - 1; i++)//冒泡趟数 { int j = 0; for (j = 0; j < sz - 1 - i; j++)//一趟的次数 { if ( 比较相邻俩元素大小 ) { 交换位置; } } } } int main() { int i = 0; int arr[10] = { 2,1,5,4,3,9,8,7,6,0 }; int sz = sizeof(arr) / sizeof(arr[0]); bubble_sort(arr, sz, sizeof(arr[0]), compare); for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++) { printf("%d ", arr[i]); } printf("\n"); return 0; }
2.2 比较相邻俩元素大小方法
冒泡排序中比较相邻元素方法是
arr[j] > arr[j + 1]
现在我们需要比较任意类型数据相邻俩元素的大小,怎么做呢?
我们发现参数中有一个比较函数指针,那么我们可以通过这个比较函数指针调用这个比较函数就能实现比较任意类型数据了,比较函数是由程序员去写,根据所需要比较的数据的类型去写比较函数,和sqort函数使用方式类似;
以上面整型数据比较为例,我们的写的比较函数返回值与参数和sqort保持一致
int compare(const void* e1, const void* e2) { return *((int*)e1) - *((int*)e2); }
e1指向的元素大于e2指向的元素返回大于0的数;等于返回0;小于返回小于0的数。
那么只要比较函数的返回值大于一,就发生交换
if (compare( , )>0) { 相邻元素发生交换; }
那么相邻俩个元素地址如何通过j表示出来呢?
这时候我们就想起函数第一个参数base,它是首元素地址,又因为base是void*指针,不能进行+,—-操作,我们可以将base强制类型转换:(char *)base;那么(char*)base+1可以向右移动一个字节位置,我们要做的是移动一个元素位置,那么一个元素是几个字节呢?
参数width就是表示一个元素几个字节大小的,那么(char*)base+j*width意思就是从首元素地址向右移动j元素,或者可以描述为第j个元素地址
代码实现
if (compare((char*)base+j*width,(char*)base+(j+1)*width)>0)
2.3 交换位置方法
仿照上面的思路,(char*)base+j*width表示第j个元素地址,(char*)base+(j+1)*width表示第j+1个元素地址,要交换这俩个元素,不妨以这俩个位置为起始地址,每次交换一个字节数据,同时向后移动一个字节地址,循环width次,那相邻俩个元素就交换完成了
void swap(void* e1, void* e2, size_t width) { int i = 0; for (i = 0; i < width; i++) { char tmp = *(char*)e1; *(char*)e1 = *(char*)e2; *(char*)e2 = tmp; ((char*)e1)++; ((char*)e2)++; } }
if (compare((char*)base+j*width,(char*)base+(j+1)*width)>0) { swap((char*)base + j * width, (char*)base + (j + 1) * width, width); }
三、总代码的实现
#include <stdio.h> #include<stdlib.h> int compare(const void* e1, const void* e2) { return *((int*)e1) - *((int*)e2); } void swap(void* e1, void* e2, size_t width) { int i = 0; for (i = 0; i < width; i++) { char tmp = *(char*)e1; *(char*)e1 = *(char*)e2; *(char*)e2 = tmp; ((char*)e1)++; ((char*)e2)++; } } void bubble_sort(void *base, size_t sz, size_t width, int(* compare)(const void* e1, const void* e2)) { int i = 0; for (i = 0; i < sz - 1; i++)//冒泡趟数 { int j = 0; for (j = 0; j < sz - 1 - i; j++)//一趟的次数 { if (compare((char*)base+j*width,(char*)base+(j+1)*width)>0) { swap((char*)base + j * width, (char*)base + (j + 1) * width, width); } } } } int main() { int i = 0; int arr[10] = { 2,1,5,4,3,9,8,7,6,0 }; int sz = sizeof(arr) / sizeof(arr[0]); bubble_sort(arr, sz, sizeof(arr[0]), compare); for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++) { printf("%d ", arr[i]); } printf("\n"); return 0; }
以上就是今天要讲的内容,学习永不停歇,靠近光,相信光,成为光!