如何对整型/浮点型/字符串/结构体等多种类型实现快速排序呢?不得不提到的一个库函数:qsort!
我们先来初步了解一下qsort:
qsort(void*base,size_t num,size_t size,int(*compar)(const void*,const void*));
qsort库函数包含四个参数。
①第一个参数void*base 也就是需要进行比较的字符串的首元素的地址。由于地址在内存中是连续存放的。得到了首元素的地址就可以顺藤摸瓜找到其他的元素了。为什么是void*类型的呢?这里可以把void*理解成一个收容所,各种各样的数据的地址都可以放在void*里面。由于qsort可以对几乎任意的类型数据进行快排,因此需要一个可以接受任意类型的指针。
②第二个参数size_t num也就是元素的个数。知道了首元素的地址,相当于知道了“起点”在哪,我们还需要知道终点才能准确地取出数据。
③第三个参数size_t size,也就是每个元素类型的长度(单位是字节)。知道了起点和终点找出了相对应的数据,但是我们需要逐个对元素进行比较才能排序。因此知道每个元素类型的长度,就可以知道每个元素。
④第四个参数int(*compar)(const void*,const void*).他的返回值是为1,0,-1.这个函数相当于比较的标准,需要我们自定义。const对其进行修饰,只进行比较,不能进行修改。这是比较的关键。
下面我们先来对不同类型的数据进行qsort,体会一下qsort是如何使用的。
一 对int类型的数据进行升序排列
#define _CRT_SECURE_NO_WARNINGS 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] = { 1,2,3,80,6,79,43,25,36,66 };
int sz = sizeof(arr) / sizeof(arr[0]);
int single_len = sizeof(arr[0]);
printf("排序之前的序列:");
for (int i = 0; i < sz ; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
qsort(arr, sz,single_len, cmp_int);
printf("排序之后的序列:");
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
二 对char类型的数据按照字典顺序进行排列
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
int cmp_char(const void* e1, const void* e2)
{
return *(char*)e1- *(char*)e2;
}
int main()
{
char arr[10] = { 'a','v','e','d','s','z','q','w','p','\0' };
int sz = sizeof(arr) / sizeof(arr[0]);
int single_lenth = sizeof(arr[0]);
printf("排序前的序列:");
for (int i = 0; i < sz; i++)
{
printf("%c ", arr[i]);
}
printf("\n");
qsort(arr, sz, single_lenth, cmp_char);
printf("排序后的序列:");
for (int i = 0; i < sz; i++)
{
printf("%c ", arr[i]);
}
return 0;
}
由于字符在内存中是按照ASCLL码表上的数字进行排列的,因此比较大小可以直接相减,类比int。
三 浮点类型的数据进行排列
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int cmp_float(const void* e1, const void* e2)
{
return ((*(float*)e1 > *(float*)e2) ? 1:-1);
}
int main()
{
float arr[] = { 1.11,5.55,4.32,6.21,7.56 };
printf("排序前的序列:");
int sz = sizeof(arr) / sizeof(arr[0]);
int single_lenth = sizeof(arr[0]);
for (int i = 0; i < sz; i++)
{
printf("%.2f ", arr[i]);
}
printf("\n");
qsort(arr,sz,single_lenth,cmp_float);
printf("排序后的序列:");
for (int i = 0; i < sz; i++)
{
printf("%.2f ", arr[i]);
}
return 0;
}
需要注意的是compar需要返回int类型的数据,所以需要转换一下。
了解了qsort的一个使用方法。能不能让冒泡排序也具有兼容性呢?
使用上qsort就可以!
下面模拟使用qsort来对冒泡排序的适用范围进行扩展。从int类型上手:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
void swap(char* buf1, char* buf2, int single_lentth)
{
for (int i = 0; i < single_lentth; i++)
{
char ret = *buf1;
*buf1 = *buf2;
*buf2 = ret;
buf1++;
buf2++;
}
}
int cmp_int(const void* e1, const void* e2)
{
return *(int*)e1 - *(int*)e2;
}
void bubble_sort(void* base, int sz, int single_lenth, int(*cmp_int)(const void* e1, const void* e2))
{
for (int i = 0; i < sz - 1; i++)
{
for (int j = 0; j < sz - 1 - i; j++)
{
if (cmp_int((char*)base + j * single_lenth, (char*)base + (j + 1) * single_lenth) > 0)
{
swap((char*)base + j * single_lenth, (char*)base + (j + 1) * single_lenth, single_lenth);
}
}
}
}
int main()
{
int arr[10] = { 1,5,3,92,5,34,78,0,66,69 };
int sz = sizeof(arr) / sizeof(arr[0]);
int single_lenth = sizeof(arr[0]);
printf("交换前:");
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
//交换前
bubble_sort(arr, sz, single_lenth, cmp_int);
printf("交换后");
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
关于if条件中为什么使用char*。因为char*解引用之后一次只访问的一个字节的数据,方便对内存中的数据进行逐一交换。如果是int*的话可以用于int类型的排序,但是兼容性并没有那么好。