目录
冒泡排序:
冒泡排序特点:
模拟&改造:
1、让冒泡排序能够接受其他的数据类型,使用参数的改造。
2、比较的方式进行改造❤
思路分析:
3、交换的代码需要改造
cmp的回调:
️最终代码演示:
http://t.csdn.cn/HQfDO
1、利用数组中的元素进行俩两比较,和循环的多次遍历进行排序,得到一个升序或者降序的排列顺序。
2、但是效率过于底下,以及通过qsort的对比,冒泡排序只能用于整型(int)数组的比较和排列,对于其他数据类型的数组比较,显得无能为力。
3、而且,效率底下,运算需要进行多次的遍历,需要设定另外的变量值,进行急刹车,才能避免不必要的遍历循环。
1、让冒泡排序能够接受其他的数据类型,使用参数的改造。
- int arr[] ——void*base 利用void*接收任意类型的地址
- int sz ——size_t sz 因为void* 所以要有sz进行元素大小的判定,以便接下来移动指针
- 增加 size_t width 因为void* 所以要有width得知每个元素的字节大小,以便接下来移动指针
- 增加int(*cmp)(const void*,constvoid*) 使用comaprt 进行函数回调,一次进行两个元素之间的大小比较
void bubble_sort(void* base, size_t sz, size_t width, int (*cmp)(const void*el, const void*e2))
2、比较的方式进行改造❤
原先的比较方式:if (arr[j] > arr[j + 1])
- 原本的比较方式是基于整型数组的原理上,进行前后元素的比较。
- 现在,数据的类型发生了改变,变成了void*这种通用类型。
- 站在程序员的角度,程序员是不明白用户在传输时究竟是哪一种的数据类型,所以不能直接指定某一种数据类型进行比较
- 这是一个 if 语句,而compart的判断原理是两个数字之间的比较返值。
- 需要交换的条件究竟是什么?是需要升序?还是需要降序?
if(cmp((char*)base+j*width,(char*)base +(j+1)*width) > 0)
思路分析:
♥ if(cmp()>0) 其实是利用了cmp的返回值原理
- return(前一个元素 减 后一个元素)
- 返回值大于0 则表示前一个元素大于后一个元素
- 返回值小于0 则表示前一个元素小于后一个元素
- 返回值等于0 则表示两个元素的数值是相等的
♥♥(char*)base+j*width
- 前文说过,站在程序员的角度,我们是不知晓用户传输的数据类型,但是对于char*来说有一个特有的优势。
- char*在进行访问字节的过程中,char*base+1 是只能访问一个字节的,而相对于其他,列如:int*base+1 是一次访问了四个字节。
- 对于char*一次只能访问一个字节的特点,我们可以利用width以及下标 j 相结合组合成一个适合char*能够从首元素地址出发,指向某个元素的地址,的运算方式。
3、交换的代码需要改造
原先交换的代码:
int tmp = arr[j];
arr[j] = arr[j + i];
arr[j + 1] = tmp;
原先交换的代码是基于整型数组的原理上进行的。- 交换时,我们的指针类型是否需要进行改变?若不需要进行再次的转变,那么该如何进行交换?
Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
在面对交换问题的同时,为了简化或是说将整个代码进行简洁化,我们采用了函数调用的方式,以此解决问题。
void Swap(char* buf1, char* buf2, size_t width)
{
int i = 0;
for (i = 0; i < width; i++)//利用遍历,进行字节和字节之间的交换
{
char tmp = *bufl;
*buf1 = *buf2;
*buf2 = tmp;
buf1++:
buf2++;
}
}
还是引用前文的话,站在程序员的角度,我们无法知晓用户传输的数据类型究竟是什么,所以在此处无法进行数据类型的转化,所以还是使用char*进行交换。
而char*在前文也有提过,是char*base+1只能访问一个字节,所以我们可以利用循环的遍历,将宽度作为循环的最大次数,以此来进行字节和字节之间的交换,直到抵达相对应的字节大小(宽度)。
int cmp(const void* e1, const void* e2)
{
return *(int*)el - *(int*)e2;
}
- 因为,cmp 在 qsor的 int (*cmp)(const void*,const void*) 以及 在前文的 if 判定返值中必须是int整型类型的。
- 也同时,cmp 是单独独立的一个函数调用,是独立的函数,进行修改时,并不会涉及其他函数的使用,所以此处便可直接进行指针数据类型的转化。
- 最后,如果要更换数据类型进行比较,也只需要修改主函数的内容和cmp的回调内容即可。
void Swap(char* buf1, char* buf2, size_t width)//交换
{
int i = 0;
for (i = 0; i < width; i++)//利用遍历,进行字节和字节之间的交换
{
char tmp = *buf1;
*buf1 = *buf2;
*buf2 = tmp;
buf1++;
buf2++;
}
}
void BB(void* base, size_t sz, size_t width, int(*cmp)(const void*, const void*))//冒泡排模拟qsort
{
int i = 0;
for (i = 0; i < sz - 1; i++)//i表示排序的趟数,和冒泡排序一样的意思
{
int j = 0;
for (j = 0; j < sz - 1 - i; j++)//j表示下摆,和冒泡排序一样的意思
{
if (cmp((char*)base + j * width,(char*)base + (j + 1) * width) > 0)//接收返回值
{
Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
}
}
}
}
int cmp(const void*e1 , const void* e2)//进行返回值
{
return *(int*)e1 - *(int*)e2;
}
void print(int* arr, int sz)//打印
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
void test1()
{
int arr[] = { 3,1,5,2,4,8,7,9,10,11,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
BB(arr, sz, sizeof(arr[0]), cmp);//进行比较的函数,cmp回调
print(arr, sz);//打印数组
}
int main()
{
test1();
return 0;
}