创建一个适用所有类型的冒泡排序函数
前言
相信大家在自己创建排序函数时,每次写出的算法只适用于单个类型,极不方便。
今天为大家带来全类型的冒泡排序函数的编写,这样既可以一步到位,又可以在同学,朋友面前装个杯,不可谓不香。
整形冒泡排序代码
void buddle_sort(int* arr,int sz)
{
for (int i = 0; i < sz;i++)
{
for (int u = 0; u < sz - i-1; u++)
{
if (arr[u] > arr[u + 1])
{
int tmp = arr[u];
arr[u] = arr[u + 1];
arr[u + 1] = tmp;
}
}
}
}
这个就是最普通的整形冒泡排序代码,现在我们要将其改写为全类型通用,我们从零开始可能有点困难,我们可以借鉴一下在c语言的库函数中qsort函数
需要什么样的参数。
这时有人可能会说,那直接用qsort函数不就行了吗,确实,在日常使用时确实可以直接使用qsort,但是自己从零开始模拟一个qsort函数的实现,不觉得很有成就感吗。
qsort解读
这里我们能发现qsort函数需要的参数:
void* base
这个就和我们整形的int* base不同,
这里需要补充一下,void*base可以存放所有类型的指针,相当于一个垃圾桶。
但是使用时不能解引用,所以需要配合强制类型转换,这个之后我们会使用到的。
size_t num
看得出来是整个数组的元素个数,和我们的整形排序函数相同。
size_t size
这个是数组中每个元素的字节大小,这个的作用在之后模拟中会详解。
接下来就是最令人难以理解的int(*compare)(const void*,const void*)
大家都能看出来这是一个函数指针,但是它的作用却匪夷所思
这里我们看一下官方的演示:
int compareMyType (const void * a, const void * b)
{
if ( *(MyType*)a < *(MyType*)b ) return -1;
if ( *(MyType*)a == *(MyType*)b ) return 0;
if ( *(MyType*)a > *(MyType*)b ) return 1;
}
这个代码看的出来这个compare是作为一个比较方法存在的,有以下作用:
1:确定需要排序的元素类型
2:确定是正序还是倒序。
qsort的使用
#include<stdio.h>
#include<stdlib.h>
//qsort 结构体使用
struct worker
{
char name[10];
int age;
};
int compare(const void* e1, const void* e2)//比较函数,需要返回整形0/1
{
return strcmp(((struct worker*)e1)->name,((struct worker*)e2)->name);
//对名字进行比较排序
}
int main()
{
struct worker man[3] = {
{"zhangwei",30},
{"sunzi",20},
{"shabi",40},
};
qsort(man,sizeof(man) / sizeof(man[1]),sizeof(man[1]),compare);
for (int i = 0; i < 3; i++)
{
printf("%s ", (man + i)->name);
printf("%d\n", (man + i)->age);
}
return 0;
}
这里是使用qsort,进行对结构体中的名字进行比较排序,这里我们能清晰的了解到了compare
函数的作用:为qsort确定了比较类型和比较方法。
仿写
按照我的讲解思路,先带大家进行对qsort的模仿,最后在编写中带大家明确讲解各个部分的各个作用,最后达到理解的目的
最后的目的是为了实现对int arr[10] = { 1,5,4,8,6,3,9,7,13,10 };
排序。
参数的仿写
void buddle_sort(void* arr,//函数部分
unsigned int sz,
unsigned int wirth,
int(*compare)(const void*,const void*)
函数部分需要以下的参数。这个时候我们在主函数中对这个函数进行传参。
int main()
{
int arr[10] = { 1,5,4,8,6,3,9,7,13,10 };
int sz = sizeof(arr) / sizeof(arr[0]);
buddle_sort(arr, sz, sizeof(arr[0]), compare);
}
这样就完成了对传参以及函数部分的编写。
compare函数的编写
现在我们就来为函数来编写compare函数。
让我们再来明确一下目的
1:确定比较的类型
2:确定正序还是倒序
int compare(const void*e1, const void* e2)
{
return *(int*)e1 - *(int*)e2;
}
因为void*使用时无法解引用,而我们的数组int arr[10]
是整形数组,
所以对e1和e2进行强制类型转换,在进行解引用即可。
buddle_sort的编写
最开始就是对原冒泡函数的基本思想:
void buddle_sort(void* arr,
unsigned int sz,
unsigned int wirth,
int(*compare)(const void*,const void*)
)
{
for (int i = 0; i < sz; i++)
{
for (int u = 0; u < sz - i - 1; u++)
{
if()
}
}
}
}
先是一个最普通的循环嵌套,接下来最重要的就是
if中的条件了
还记得前面我们写的compare函数吗,此时此刻正是它发挥作用的时候,但是写法却是个大问题。
此时此刻我们需要实现
arr[1]比较arr[2]->
arr[2]比较arr[3]->
arr[3]比较arr[4]…………
就是说我们需要给compare传递的是arr[i],arr[i+1]
那compare(arr[i],arr[i+1])
这个就是正确的吗?
还记得这个arr现在是void类型的指针吗
所以说指针的运算无法进行
现在让我们回头看函数的参数:unsigned int wirth
我们特意传了每个元素的大小,这个时候正是它排上用场的时候
提问:哪个指针类型,搭配上每个元素的大小通过运算就能实现对每个字节的改变和调用?
答案正是char*
—因为它进行运算时以每个字节为运算量。
所以这里就能有答案了
for (int i = 0; i < sz; i++)
{
for (int u = 0; u < sz - i - 1; u++)
{
if (compare((char*)arr+wirth*u,
(char*)arr + wirth*(u+1))>0)
{
);
}
}
}
将每个元素的指针传过去后与compare函数中
:return *(int*)e1 - *(int*)e2;
相匹配
将指针转换为int类型,重新获得访问权限,从而实现整个元素的比较。
exchange的编写
if的条件编写完了,接下来距离整个函数的完成只剩下最后一步了——交换函数的编写。
有了每个元素的大小,有了需要交换的元素的指针,接下来就很轻松就可以解决了
void exchange(char* z,char*p,int wirth)
{
for (int i = 0; i < wirth; i++)
{
int tmp = *z;
*z = *p;
*p = tmp;
z++;
p++;
}
}
整体代码
#include<stdio.h>
void exchange(char* z,char*p,int wirth)
{
for (int i = 0; i < wirth; i++)
{
int tmp = *z;
*z = *p;
*p = tmp;
z++;
p++;
}
}
void buddle_sort(void* arr,
unsigned int sz,
unsigned int wirth,
int(*compare)(const void*,const void*)
)
{
for (int i = 0; i < sz; i++)
{
for (int u = 0; u < sz - i - 1; u++)
{
if (compare((char*)arr+wirth*u,
(char*)arr + wirth*(u+1))>0)
{
exchange((char*)arr + wirth * u,
(char*)arr + wirth * (u + 1),
wirth
);
}
}
}
}
int compare(const void*e1, const void* e2)
{
return *(int*)e1 - *(int*)e2;
}
int main()
{
int arr[10] = { 1,5,4,8,6,3,9,7,13,10 };
int sz = sizeof(arr) / sizeof(arr[0]);
buddle_sort(arr, sz, sizeof(arr[0]), compare);
for (int i = 0; i < 10; i++)
{
printf("%d\n", arr[i]);
}
return 0;
}