目录
算法,是程序的灵魂。排序算法是典型的算法之一,我们初识c语言时,数组排序是一大难点。我们开始识得冒泡排序算法(bubble_sort)。但冒泡排序的缺点也很明显,那就是只能排序整形数组。
那我们想一下,如果是字符数组呢?比如要比较abcdefg...
再深度思考一下,如果是结构体比较呢?比如要比较年龄,名字等等。
要做到如上的排序实现,可以对冒泡排序进行一个优化,那就是库中的qsort函数。
这个函数可牛了,什么类型都可以排序。让我们用一个专业网站查询一下吧!cplusplus.com - C++资源网络https://legacy.cplusplus.com/ 下图是qsort函数的参数及其功能
下图是qsort函数的应用实例
10 20 25 40 90 100
这样就用qsort成功排序好了一个整形数组。
分析一下qsort函数的参数。站在一个设计师的角度,要排序一个用户需要的数组,那肯定要知道以下几点。我们来对它们分个工吧!
首先要注意引头文件<stdlib.h>
一. 既然是要排数组,那数组元素的地址就需要有一个指针来接收。void*base就负责存放待排序数据第一个对象的地址。
二. 有了数组的地址,排序数据元素的个数也必不可少,size_t num就负责统计和存放数组元素的个数。
前两点也是冒泡排序所需要的参数,那么接下来的参数,就是qsort独有的!
三. 因为bubble_sort专门针对整形数组,所以默认一个元素跳过一个整形(也就是4个字节)。但是如果是其他类型,则需要告诉函数一个数据元素的大小,由size_t size来统计和存放。
四. 最后一个参数也是最难理解的部分。int (*compar) (const void *, const void *)首先它是一个函数指针,指向一个compar函数,这个函数是用来比较两个元素大小的函数,函数的参数是两个const void*——用指针来实现两个数据元素大小的比较,void*根据类型强制转换成想要排序类型的指针(比如例子中的强制转换成(int*))。int 是函数的返回值,两个元素比较,前大返回正数,前小返回负数。这样剖析会更好理解吧!
来排序一下结构体数据过过瘾吧!
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct stu
{
char name[20];
int age;
};
int sort_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",30},{"lisi",31},{"wangwu",34} };
int sz = sizeof(s) / sizeof(s[0]);
qsort(s, sz, sizeof(s[0]), sort_by_name);
return 0;
}
这样,我们对qsort的使用就了如执掌啦!当然,我们的目的在于模拟实现,也就是以开发者的角度在<stdlib.h>这个库中写入我们自己的qsort。如果清楚了怎么使用,自己写起来当然会更得心应手一些,当然也不是一件简单事,下面跟着我一起设计这个函数吧!
my_qsort的启程之路
首先,参数无疑,模仿着qsort来即可。
把主函数写好,这里还是以整形数组为例。(数组元素是我随便输入的,越随意测试效果越明显哈哈哈)
int main()
{
int arr[] = { 2, 51,63,723,8,12832,82,72,8 };
int sz = sizeof(arr) / sizeof(arr[0]);
my_qsort(arr, sz, sizeof(arr[0]), cmp);
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
冒泡排序的趟数还是不变的,有变化的地方就是比较两个数据大小,也就是区别不同类型的地方,如下图标出来的①,②。
比较两个数据大小可就是一个难点了。如何判断两个数据的大小,如何实现两个数据的交换是我们要突破的。
我们第一个参数中拿到了数组首元素的地址,base指针指向了这个地址。
要比较前后两个数据的大小,是让指针+多少能表示一个元素的跨度呢?如上图,base指针类型是任意的,如果是整形指针,那么base+1就是跳过整形的一个数据,而char数组就是跳过4个数据。这样就不能达到比较大小的效果。那么我们应该怎么做呢?
既然最小的字节是1,那么不妨将base转换为char*的指针,然后根据元素的宽度进行base的加减。如下图:
cmp函数也就是我们自己根据数组类型写的比较大小的函数,如下图:
这样就圆满完成比较大小的任务啦!那么比较大小之后,肯定就是交换了。交换也是一种细活呢。
我们写一个Swap函数来实现两个数据的交换。函数的参数跟cmp函数一样,但是多了个宽度。为什么呢?比较两个元素大小的话,只需要找到两个元素即可,如果要实现两个元素的交换的话,我们做不到。这时只需要将元素的每个字节都进行交换就能达到效果。如下图:
我们可以将Swap函数这样写:
Swap(char* buf1, char* buf2, int width)
{
int i = 0;
for (i = 0; i < width; i++)
{
char tmp = *buf1;
*buf1 = *buf2;
*buf2 = tmp;
buf1++;
buf2++;
}
}
这样我们的my_qsort函数就模拟实现完成啦!是不是很酷呢!来看看效果吧!