前言
本篇是通过qsort的学习,改进冒泡排序,如果有不懂qsort的可以去我前面的文章看看
回调函数与用qsort排各种类型的数据
一、冒泡排序
void bubble_sort(int arr[], int len)
{
int i, j, temp;
for (i = 0; i < len - 1; i++)
{
for (j = 0; j < len - 1 - i; j++)
{
if (arr[j] > arr[j + 1])
{
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
我们能看到经典的冒泡排序需要我们指定一个排序类型,不够通用,不能封装成一个函数
二、qsort函数
void qsort( void *base, size_t num, size_t width, int (__cdecl *compare )(const void *elem1, const void *elem2 ) );
我们可以看到qsort函数之所以可以封装成为函数,是因为其的通用性,void*类型的指针就决定了其的通用性
三 、冒泡排序的改进
通过对qsort的研究,我们可以参考qsort对其进行修改
(1)我们可以对冒泡排序的形参就行修改
void bubble_sort(void* base, size_t sz, size_t width, int (*cmp)(const void* e1, const void* e2))
(2)但是我们需要保持冒泡排序的特征,所以趟数是不会变的
(3)我们使用了void类型的指针,那就意味着我们需要对于swap进行修改,不然会造成数据丢失
(4)我们使用了void函数,我们通过步长移动的时候我们怎么控制其的移动呢,我们得到了width,那么我们是不是可以强制转换数据为char类型,这样我们移动width是不是就可以精准的移动到下一个元素的首字节上去
其实冒泡排序的修改最关键的就是对于swap的修改,怎么修改才能不造成数据丢失呢?我们想想我们要width是想干嘛,width是告诉我们我们传的数据的字节数,我们通过数组的首地址和数组的每个元素的字节大小是不是就可以知道数据的每个元素,同时我们甚至可以修改每个字节,其实这里我们也可以看出指针的好处(哈哈哈,题外话),既然我们知道了每个字节的位置,那么我们是不是可以通过修改每个元素的每个字节,从而修改这个数据
举个例子一个int 类型的数据强制转换成为char类型的数据,我们将int的4个字节全部完整的赋值到另外一个int 数据上去,是不是就不会造成数据丢失了呢,通过这种思路我们可以将swap修改成
void 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++;
}
}
至此,我们就将冒泡排序修改完成了
四 、最终代码
void Swap(char* buf1, char* buf2, int width)
{
int i = 0;
for (i = 0; i < width; i++)//这里的swap交换的是每个元素的每个字节
{
char tmp = *buf1;
*buf1 = *buf2;
*buf2 = tmp;
buf1++;
buf2++;
}
}
int cmp_int(const void* e1, const void* e2)
{
return *(int*)e1 - *(int*)e2;
}
//改造冒泡排序函数,使得这个函数可以排序任意指定的数组
void bubble_sort(void* base, size_t sz, size_t width, int (*cmp)(const void* e1, const void* e2))
{
//趟数
size_t i = 0;
for (i = 0; i < sz - 1; i++)
{
//一趟冒泡排序的过程
size_t j = 0;
for (j = 0; j < sz - 1 - i; j++)
{
if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
{
//交换
Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
}
}
}
}