目录
前言
我们之前学过的冒泡排序只能进行对整数类型的排序,冒泡排序代码如下:
void BubbleSort(int array[],int len)
{
int tem;
//外层循环控制 排序的趟数 n个元素排序需要循环n-1次
for(int i=0;i<len-1;i++)
{
//内层循环控制比较的次数 n个元素第i趟比较n-i次
for(int j=0;j<len-1-i;j++)
{
//比较相邻的元素大小 目的:将最大的元素选出到移动到最后
if(array[j]>array[j+1])
{
tem = array[j];
array[j] = array[j+1];
array[j+1] = tem;
}
}
}
}
上述冒泡排序的代码中,其整型的比较方式为“array[j]>array[j+1]”,但如果换一种数据类型很明显再使用上述方法比较两个元素是错误的。而我们今天要学习的qsort函数就能解决元素类型不同(注:两个相互比较的元素类型必须相同)而引起的问题。其使用的算法思想为:快速排序
一、qsort函数基本介绍
我们先来看看qsort函数参数分别代表什么意思:(下图截至MSDN)
- ① void* base — 目标数组地址
- ② size_t num — 数组元素个数
- ③ size_t width — 一个元素大小(字节)
- ④ int (*cmp) (const void* e1, const void* e2) — 比较函数 (需要使用者自己编写) int (__cdecl *compare ) 省略不影响
二、qsort函数的使用
由于第四个参数是比较函数,需要我们自己编写,根据比较元素类型的不同编写不同的比较函数。
在写这个代码之前我们先来学习一个知识点:void* — 无类型指针 — 接收任意类型变量的地址
char ch = ‘w’;
void* p = &ch; //不会报错
所以为了能够接收任意类型地址,使用void*更合适。
错误的使用:
char ch = ‘w’;
void* p = &ch;
*p = ‘s’; //报错
由于void* 是无(具体)类型的指针,而解引用时不知到访问几个字节,因此void* 类型的指针是不能进行解引用的,同理也不能进行+1整数的操作。
理解了void* 的内容就容易理解设计qsort函数时为什么使用void* 来接收了。
1、qsort在整型比较中的使用
根据上面所说的参数含义我们先使用案例来为大家讲解qsort函数的使用:
使用qsort排序数组:arr[10] = { 9,8,7,6,5,4,3,2,1}
#include <stdio.h>
#include<stdlib.h>
int cmp_int(const void* e1, const void* e2) //接收要比较两个元素的地址
{
//比较两个整型值的函数
return *(int*)e1 - *(int*)e2;
}
int main()
{
int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), cmp_int);
return 0;
}
代码分析:
- 根据前面的void* 的知识,由于void*可以接收所有类型的指针,而为了使qsort函数能够排序所有的元素类型,所以使用void* 作为形参接收实参。
- 比较函数的返回值及其代表含义如下: 当返回值小于0时,e1 < e2;当返回值等于0时,e1 = e2;当返回值大于0时,e1 > e2 这就是为什么要设计两个元素相减
- 由于前面说到过void*类型的指针不能进行解引用操作,由于我们比较的是两个整型值,所以我们先将void* 类型的指针进行强制类型转换为int*:(int*)e1,再将整型指针解引用相减:*(int*)e1- *(int*)e2
- 函数名为函数地址,即实参中只写函数名即可
运行结果如下(上面代码漏了打印语句):
2、qsort在浮点型比较中的使用
有了前面的基础,在后面的类型中的使用大同小异(仅仅是在比较函数中,类型不同元素之间的比较方式不同),不做过多叙述。
我们再来看看浮点型的比较:
使用qsort排序数组:arr[10] = { 9.1,8.5,7.5,6.7,5.1,4.4,3.1,2.3,1.1}
代码如下:(不含主函数)
int cmp_float(const void* e1, const void* e2) //接收要比较两个元素的地址
{
//比较两个浮点型值的函数
if (*(int*)e1 == *(int*)e2)
{
return 0;
}
else if (*(int*)e1 < *(int*)e2)
{
return -1;
}
else if (*(int*)e1 > *(int*)e2)
{
return 1;
}
}
由于函数的返回类型为int,而两个浮点型相减结果为浮点型,因此需要进行一些处理,不能直接return。若想直接return可以最后进行强制类型转换为int
3、qsort在结构体比较中的使用
由于结构体是一个复杂的数据类型,所以需要通过里面一种元素进行比较。
使用qsort排序下面的结构体:
struct Stu
{
char name[20];
int age;
};struct Stu s[3] = { {"zhangsan",20},{"lisi",30},{"wangwu",10} };
1)根据年龄比较
#include <stdio.h>
#include<stdlib.h>
struct Stu
{
char name[20];
int age;
};
int cmp_stu_by_age(const void* e1, const void* e2)
{
return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
int main()
{
struct Stu s[3] = { {"zhangsan",20},{"lisi",30},{"wangwu",10} };
int sz = sizeof(s) / sizeof(s[0]);
qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);
for (int i = 0; i < 3; i++)
{
printf("%s %d\n", s[i].name, s[i].age);
}
return 0;
}
由于年龄是int类型的变量,只需要将e1与e2转换成结构体类型,再访问年龄相减即可。
2)根据名字比较
#include <stdio.h>
#include<stdlib.h>
#include<string.h>
struct Stu
{
char name[20];
int age;
};
int cmp_stu_by_name(const void* e1, const void* e2)
{
return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
int main()
{
struct Stu s[3] = { {"zhangsan",20},{"lisi",30},{"wangwu",10} };
int sz = sizeof(s) / sizeof(s[0]);
qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);
for (int i = 0; i < 3; i++)
{
printf("%s %d\n", s[i].name, s[i].age);
}
return 0;
}
比较名字就是比较字符串,字符串比较不能直接使用 > < =直接比较,应该使用strcmp函数。
三、使用回调函数,模拟实现qsort(采用冒泡的方式)
#include <stdio.h>
int int_cmp(const void* p1, const void* p2)
{
return (*(int*)p1 - *(int*)p2);
}
void _swap(void* p1, void* p2, int size)
{
int i = 0;
for (i = 0; i < size; i++)
{
char tmp = *((char*)p1 + i);
*((char*)p1 + i) = *((char*)p2 + i);
*((char*)p2 + i) = tmp;
}
}
void bubble(void* base, int count, int size, int(*cmp)(void*, void*))
{
int i = 0;
int j = 0;
for (i = 0; i < count - 1; i++)
{
for (j = 0; j < count - i - 1; j++)
{
if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0)
{
_swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
}
}
}
}
int main()
{
int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
int i = 0;
bubble(arr, sizeof(arr) / sizeof(arr[0]), sizeof(int), int_cmp);
for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
{
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}