目录
qsort()函数的功能:
首先回忆一下冒泡排序是如何实现的
需要改动的地方:
compare():
swap():
qosrt()函数实现
快速排序实现qsort()已经成功
今天我要分享的是qsort的自主实现。
冒泡版qsort()(标准是用的快速排序,好吧,我还没想到怎么准确实现)
qsort()函数能将一个数组的元素进行排序。
作用:
Remarks
The qsort function implements a quick-sort algorithm to sort an array of num elements, each of width bytes. The argument base is a pointer to the base of the array to be sorted.qsort overwrites this array with the sorted elements. The argument compare is a pointer to a user-supplied routine that compares two array elements and returns a value specifying their relationship. qsort calls the compare routine one or more times during the sort, passing pointers to two array elements on each call:
翻译:
评论
qsort函数实现了一种快速排序算法,可以对num元素数组进行排序,每个元素的宽度为字节。参数base是指向要排序的数组的基的指针。qsort用已排序的元素覆盖此数组。参数compare是指向用户提供的例程的指针,该例程比较两个数组元素,并返回一个指定其关系的值。qsort在排序过程中调用比较例程一次或多次,每次调用时将指针传递到两个数组元素:
简单来说就是排序,但是你要自己写一个比较大小的函数。
注意:这个函数的函数指针的类型为 int (*)(const void *, const void * );
如果不知道函数指针就别管这个了(以后我会出一篇详解指针文章)。
先看一下qsort函数的类型(带参数名字)
可复制:
void qsort( void *base, size_t num, size_t width, int (__cdecl *compare )(const void *elem1, const void *elem2 ) );
由于qsort只是让待排序数组排序,所以不必设置返回值,返回类型为空。base是传入的首元素地址,void*说明它可以接收任意类型的地址;num是待排数组的元素个数,size_t是unsigned int类型的重定义类型,其实就是unsigned int(上一篇文章提到了),元素个数不可能是负数,所以用的是无符号类型;width是数组每一个元素的所占空间的大小;int (__cdecl *compare )(const void *elem1, const void *elem2 ) 是一个函数指针(不知道函数指针也没事,看案例就可明白)。
先看一下标准qsort函数的应用:
#include
#include
int compare1(const void* e1, const void* e2)
{
return (int)(*(int*)e1 - *(int*)e2);
}
int main(void)
{
int arr[5] = { 2,4,3,1,0 };
int i = 0;
int j = 0;
for (i = 0; i < 5; i++)
{
printf("%d ", arr[i]);
}
int len = sizeof(arr) / sizeof(arr[0]);
printf("\n======================\n");
qsort(arr, len, sizeof(int), compare1);
for (i = 0; i < 5; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
运行结果:
自主实现(利用冒泡排序):
for (i = 0; i < len - 1; i++)
{
for (j = 0; j < len - 1 - i; j++)
{
if (arr[j] > arr[j + 1])
{
int t = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = t;
}
}
}
我们就以这个为基础来实现,
一:if的判断条件。
二:if里面需要设立一个函数实现数组元素的交换。
这里用两个函数,if的判断条件用compare()函数的返回值来加以修饰来作为判断条件,而swap()函数作为交换两个元素的函数。
于是我们可以改写为:
以每个元素为int类型的数组为例:compare()函数是是自己设置的,目的是比较两个元素的大小。我们先实现这个函数的实现。
因为我们需要对待排数组排序,所以我们应该知道待排数组的每个元素的类型是什么,以int类型为例:
(int*)e1是把e1前置类型转换为int*类型,而*(int*)e1是对强制类型转换后的e1解引用,就是取出元素的值,那么*(int*)e1 - *(int*)e2就是两个元素相减,有人可能会问了,这个结果不就是int类型吗,为什么还要对最终结果强制类型转换为int?因为我想以后编写其他类型的元素的数组方便。一会换其他类型的实例就很容易明白(升序是e1在前,降序是e1在后,主要是返回的结果与0比较)。
判断条件:
compare(前一个元素, 后一个元素) > 0
因为我们是用冒泡排序的思想,传入的是相邻前后的元素,这个函数的返回值如果大于0那么就交换。
swap()需要交换的元素类型是未知的,所以用void*类型来接收,但是如果写成void*我们怎么对元素进行交换呢?我们学过的最小类型是char一个字节,数据的是以字节为单位在计算机里存储的,那么我们有一个思路,我们可以先把void*强制类型转换为char*类型,一个字节一个字节的进行交换。但是我们还要知道每个元素占用多少字节,即交换几个字节,要做到恰好两个元素每个字节都交换,所以我们要传入一个元素所占用字节数,我们可以用传入qsort()的形参width作为swap()的一个实参,代表一个元素的所占字节数。
那么swap()函数就可以这样实现:
void swap(char* c1, char* c2, int len)
{
int i = 0;
for (i = 0; i < len; i++)
{
char t = *(c1 + i);
*(c1 + i) = *(c2 + i);
*(c2 + i) = t;
}
}
char* c1, char* c2表示我们直接把传进来元素的地址以char*类型进行操作。
现在我们的代码大概可以写出
for (i = 0; i < num - 1; i++)
{
for (j = 0; j < num - 1 - i; j++)
{
if (前一个元素, 后一个元素) > 0)
{
swap(前一个元素, 后一个元素, width);
}
}
}
现在我们有了一种思想,char*类型可以解决我们遇到的问题。
所以copare()和swap()传入数组元素的地址怎么传入合理呢?
答案是先转换为char*类型在利用j和width寻找代操作元素。
用compare()举例:
compare((char*)base + j * width, (char*)base + (j + 1) * width)
(char*)base + j * width 的含义是先以char*找到base地址,然后移动j*width个char类型距离,这里就相当于base[j],但是不能用base[j]直接访问,因为传入base是未知的类型,不可直接访问base的某个元素
那么(char*)base + (j + 1) * width就相当于base[j + 1]。
所以我们可以写出swap()函数的传参
swap((char*)base + j * width, (char*)base + (j + 1) * width, width)
void swap(char* c1, char* c2, int len)
{
int i = 0;
for (i = 0; i < len; i++)
{
char t = *(c1 + i);
*(c1 + i) = *(c2 + i);
*(c2 + i) = t;
}
}
void my_qsort(void* base, size_t num, size_t width,
int(__cdecl* compare)(const void* e1, const void* e2))
{
assert(base);
int i = 0;
int j = 0;
for (i = 0; i < num - 1; i++)
{
for (j = 0; j < num - 1 - i; j++)
{
if (compare((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
{
swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
}
}
}
}
看一下成果:
示例一(int):
#include
#include
int compare1(const void* e1, const void* e2)
{
return (int)(*(int*)e1 - *(int*)e2);
}
void swap(char* c1, char* c2, int len)
{
int i = 0;
for (i = 0; i < len; i++)
{
char t = *(c1 + i);
*(c1 + i) = *(c2 + i);
*(c2 + i) = t;
}
}
void my_qsort(void* base, size_t num, size_t width,
int(__cdecl* compare)(const void* e1, const void* e2))
{
assert(base);//防止传入空指针,保证代码的健壮性
int i = 0;
int j = 0;
for (i = 0; i < num - 1; i++)
{
for (j = 0; j < num - 1 - i; j++)
{
if (compare((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
{
swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
}
}
}
}
int main(void)
{
int arr[5] = { 2,4,3,1,0 };
int i = 0;
int j = 0;
for (i = 0; i < 5; i++)
{
printf("%d ", arr[i]);
}
printf("\n======================\n");
int len1 = sizeof(arr) / sizeof(arr[0]);
my_qsort(arr, len1, sizeof(int), compare1);
for (i = 0; i < 5; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
运行结果:
示例二(char):
#include
#include
#include
int compare2(const void* e1, const void* e2)
{
return (int)(*(char*)e1 - *(char*)e2);
}
void swap(char* c1, char* c2, int len)
{
int i = 0;
for (i = 0; i < len; i++)
{
char t = *(c1 + i);
*(c1 + i) = *(c2 + i);
*(c2 + i) = t;
}
}
void my_qsort(void* base, size_t num, size_t width,
int(__cdecl* compare)(const void* e1, const void* e2))
{
assert(base);//防止传入空指针,保证代码的健壮性
int i = 0;
int j = 0;
for (i = 0; i < num - 1; i++)
{
for (j = 0; j < num - 1 - i; j++)
{
if (compare((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
{
swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
}
}
}
}
int main(void)
{
char ch[5] = "adxb";
int i = 0;
int j = 0;
printf("%s", ch);
printf("\n======================\n");
int len2 = strlen(ch);
my_qsort(ch, len2, sizeof(char), compare2);
printf("%s", ch);
return 0;
}
运行结果:
示例三(double):
#include
#include
int compare3(const void* e1, const void* e2)
{
return (int)(*(double*)e1 - *(double*)e2);
}
void swap(char* c1, char* c2, int len)
{
int i = 0;
for (i = 0; i < len; i++)
{
char t = *(c1 + i);
*(c1 + i) = *(c2 + i);
*(c2 + i) = t;
}
}
void my_qsort(void* base, size_t num, size_t width,
int(__cdecl* compare)(const void* e1, const void* e2))
{
assert(base);//防止传入空指针,保证代码的健壮性
int i = 0;
int j = 0;
for (i = 0; i < num - 1; i++)
{
for (j = 0; j < num - 1 - i; j++)
{
if (compare((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
{
swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
}
}
}
}
int main(void)
{
double dd[5] = { 0.2, 0.3, 5.0, 0.1, -10.2 };
int i = 0;
int j = 0;
for (i = 0; i < 5; i++)
{
printf("%lf ", dd[i]);
}
printf("\n======================\n");
int len3 = sizeof(dd) / sizeof(dd[0]);
my_qsort(dd, len3, sizeof(double), compare3);
for (i = 0; i < 5; i++)
{
printf("%lf ", dd[i]);
}
return 0;
}
运行结果:
实例四(结构体):
#include
#include
typedef struct Student
{
char name[10];
double scores;
} Student;
int compare4(const void* e1, const void* e2)
{
return (int)(((Student*)e1)->scores - ((Student*)e2)->scores);
}
void swap(char* c1, char* c2, int len)
{
int i = 0;
for (i = 0; i < len; i++)
{
char t = *(c1 + i);
*(c1 + i) = *(c2 + i);
*(c2 + i) = t;
}
}
void my_qsort(void* base, size_t num, size_t width,
int(__cdecl* compare)(const void* e1, const void* e2))
{
assert(base);//防止传入空指针,保证代码的健壮性
int i = 0;
int j = 0;
for (i = 0; i < num - 1; i++)
{
for (j = 0; j < num - 1 - i; j++)
{
if (compare((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
{
swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
}
}
}
}
int main(void)
{
Student s[5] = { {"张三", 10}, {"李四", 0}, {"王五", 100}, {"乌龟", 0.5}, {"兔子", -20} };
int i = 0;
int j = 0;
for (i = 0; i < 5; i++)
{
printf("%-6s%lf\n", s[i].name, s[i].scores);
}
printf("\n======================\n");
int len4 = sizeof(s) / sizeof(s[0]);
my_qsort(s, len4, sizeof(s[0]), compare4);
for (i = 0; i < 5; i++)
{
printf("%-6s%lf\n", s[i].name, s[i].scores);
}
return 0;
}
运行结果:
这篇文章本来还想这再用快速排序来实现,但是没实现~~~,我会努力让它实现的!
要多记录一下写的代码了,伙伴们的支持是我最大的动力。
qosrt()快速排序自主实现_逆风路上伴有谁的博客-CSDN博客