目录
前言:
我们在模拟qsort函数的时候,我们要联想到当初作者实在怎么样的一个环境下,所书写的代码。作者在书写的时候,是不知道用户会用什么样的来进行比较,并排序,所以我们所要注意,我们要巧妙的运用 void* 与 char* 的特性来,进行完成我们所模拟的代码。
1 :将冒泡排序与qsort联系:
首先以一个冒泡排序完成排序代码。
#include <stdio.h>
void bubble(int* arr, int sz)
{
for (int i = 0; i < sz - 1; i++)
{
for (int 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;
}
}
}
}
void print(int* arr, int sz)
{
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
int main()
{
int arr[10] = { 10,9,8,7,6,5,4,3,2,1 };
int sz = sizeof(arr) / sizeof(arr[0]);
bubble(arr, sz);
print(arr, sz);
printf("\n");
return 0;
}
1.1 :参数接收变化:
我们可以发现,冒泡排序所必须的就是传入一个字符串的起始地址,便于向后寻找数据,但是由于函数不知数组的范围,位置需传入一个sz,这样的情况下才能保证遍历每个元素。但这是基于我们知道传的什么类型的数据,并将其书写为 " void bubble(int* arr, int sz) " 。我们要知道,qsort函数的作者,能不能想到我们所使用qsort会排序怎样的一个具体的数据类型的数据?那当然是不知道的。于是我们就要针对其经行修改。
1.1.1 :" int* arr "
我们不知道他的类型,但是首元素又要经进行传参,于是将其变为:
void* arr, int width
以 " void* " 进行直接的接受,并让其不具备任何的类型,这样我们就得到了首元素的地址,并且可以接收任意的类型的数组。但是,要知道这样的指针类型,根本无法告诉函数一个元素的大小的,这会导致使得后方的冒泡排序的调换部分出现问题,之前的冒泡排序因为,以清晰的指针类型进行了接受,可以进行完美的调换。但是这里不行,于是我们需要将一个元素的宽度传入。
1.1.2 :" int sz " 与 比较函数
对于 " int sz " 因为对于任意类型的函数都可以兼容的传入,无需考虑其的问题,于是不变即好。
对于比较函数,再qsort中就是我们必须要写的,这里就以自行创造bubble_qsort冒泡排序函数的角度从零到一:
- 比较是两个元素之间需进行的,所以需要 e1,e2 。
- 对于两个元素我们只是进行比较,并不进行改变,于是二者皆以 const 定义。
- 在冒泡排序中有一个if语句,用于判断二者是否需要进行调换。于是我们将:前者大于后者返回>0;前者等于后者返回=0;前者小于后者返回<0。这样便有了比较函数。
参数接收变化如下:
int sz ,int cmp_int(const void* e1, const void* e2)
比较函数如下(以int类型的数据为例):
int cmp_int(const void* e1, const void* e2)
{
return (int*)e1 - (int*)e2;
}
1.1.3 :变为
void bubble_qsort(void* arr, int sz , int width,int cmp_int(const void* e1, const void* e2))
{
;//此处省略实现代码
}
1.2 :冒泡函数内部变化
void bubble(int* arr, int sz)
{
for (int i = 0; i < sz - 1; i++)
{
for (int 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;
//-----------------------第二处
}
}
}
}
1.2.1 :第一处
此处我们要将所需要比较的内容数据传参给 cmp_int 函数,但是,我们要知道,在 bubble_qsort 函数接受中 , 我们将arr首元素的地址以 void*的指针进行的接受。对于cmp_int 函数所需要多次的使用,比较不同组的数据,但是由于,其没有实际的类型的,无法进行直接使用(指针的解引用与加减等)。于是,我们需要对其去进行强制类型转换。但是,对于各种的类型而言,其的字节大小皆是>=1的,因此我们最佳的选择就是将其强制类型转换为 char* 的指针。这样我们将元素的地址进行输出,用户再从cmp_int 函数中对其强制类型转换为其对应的类型进行比较即可。
对于,例如:啊arr[0]与arr[1], arr[1]与arr[2]。这样的多组比较,在原冒泡排序中为 " arr [ j ] > arr [ j + 1 ] " 。然而对于以转换为 char* 指针来说,这种方法肯定是不行的了,因为原来是具备其真实的类型的。所以之前说传入的width就起作用了,width 可以表示为,第一个元素的地址到第二个元素的地址,其距离的字节距离。
void bubble(int* arr, int sz)
{
for (int i = 0; i < sz - 1; i++)
{
for (int j = 0; j < sz - 1 - i; j++)
{
//----------------------------------------------------------------
if (cmp_int((char*)arr + j * width, (char*)arr + (j + 1) * width))
{//----------------------------------------------------------------第一处
int tmp = arr[ j ];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
}
1.2.2 :第二处
在原本的冒泡排序中,可以进行直接的调换,是因为函数清楚的知道其的类型。在bubble_qsort 函数中同样的问题,于是也选择为转换char*类型,并以同样的方式转化:
void bubble(int* arr, int sz)
{
for (int i = 0; i < sz - 1; i++)
{
for (int j = 0; j < sz - 1 - i; j++)
{
if (cmp_int((char*)arr + j * width, (char*)arr + (j + 1) * width))
{
//---------------------------------------------------------------------
Swap((char*)arr + j * width, (char*)arr + (j + 1) * width, width);
//---------------------------------------------------------------------第二处
}
}
}
}
1.3 :对于Swap(交换)函数的构建
此处,我们以*e1 = 10000000,*e2 = 9000000为例。。那么为了类型的多样化,我们可以将地址以char*类型的指针接受。然后更具一个字节为一个单位进行交换:80与40换,96与54换,98与89换,00与00换。这样就相当于int类型的数据进行交换。
void Swap(char* e1, char* e2, int width)
{
for (int i = 0; i < width; i++)
{
char tmp = *e1;
*e1 = *e2;
*e2 = tmp;
e1++;
e2++;
}
}
2 :最终代码
#include <stdio.h>
#include <stdlib.h>
//比较大小
int cmp_int(const void* e1, const void* e2)
{
return (int*)e1 - (int*)e2;
}
//数据交换
void Swap(char* e1, char* e2, int width)
{
for (int i = 0; i < width; i++)
{
char tmp = *e1;
*e1 = *e2;
*e2 = tmp;
e1++;
e2++;
}
}
//冒泡排序
void bubble_qsort(void* arr, int sz , int width,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*)arr + j * width, (char*)arr + (j + 1) * width))
{
Swap((char*)arr + j * width, (char*)arr + (j + 1) * width, width);
}
}
}
}
//数组输出
void print(int* arr, int sz)
{
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
int main()
{
int arr[10] = { 10,9,8,7,6,5,4,3,2,1 };
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_qsort(arr, sz, sizeof(arr[0]),cmp_int);
print(arr, sz);
printf("\n");
return 0;
}