目录
前言
这篇文章中你能学习到冒泡排序的思想,以及它的实现方式。了解到qsort()函数的使用,以及怎么用冒泡排序的实现模拟实现qsort()函数。这篇文章需要掌握一定的指针知识,如果有不够熟悉指针的朋友可以看看我往期指针的文章,有助于加深理解。
一,冒泡排序
1.1冒泡排序思想
对一个无序数组的第一个元素的位置开始,进行两两比较,根据大小交换位置,直到最后将最大 (小)的数交换到了无序队列的队尾,第一趟结束后,第二趟依旧从第一个元素开始排起排到倒数第二个元素结束,第三趟排到到数第三个元素结束,依次类推。
为什么第二趟要到倒数第二个结束,第三个要到倒数第三个元素结束呢?
因为每一趟排序就会把最大(小)值就会放在序列的最后,所以第一趟排完,最大(小)值就已经在最后了。
//这里使用冒泡排序排一组无序数列(降序)
3 5 4 7 1 8 9 2 0 6
先从第一个数 3 开始,与下一个数比较大小,不满足条件,换位置,接着与下一个数继续比较,直到交换的队尾
5 3 4 7 1 8 9 2 0 6
//这里为了满足降序3 和 5交换
5 4 3 7 1 8 9 2 0 6
//接着,3与下一位继续比较
5 4 7 3 1 8 9 2 0 6
......
//第一趟排完
5 4 7 3 8 9 2 1 6 0
//第二趟
5 4 7 3 9 8 2 1 6 0
5 4 7 3 9 8 2 6 1 0
//第三趟,第四趟.......第九趟。
一个有n个元素的序列,需要排n-1趟。
1.2 代码实现
#include<stdio.h>
//bubble_sort
void bubble_sort(int arr[10], int sz)
{
int i = 0;
for (i = 0; i < sz - 1; i++) //总共遍历sz-1趟
{
int j = 0;
for (j = 0; j < sz - i - 1; j++)//每趟进行比较
{
if (arr[j] < arr[j + 1]) //两两比较小的数放后面
{
int tmp = arr[j + 1];
arr[j + 1] = arr[j];
arr[j] = tmp;
}
}
}
for (i = 0; i < sz; i++) //排完打印
{
printf("%d ", arr[i]);
}
return 0;
}
int main()
{
int arr[10] = { 5,4,7,3,1,8,9,2,0,6 };
int sz = sizeof(arr) / sizeof(arr[0]);//元素的个数
bubble_sort(arr, sz);
return 0;
}
二,qsort()函数
冒泡排序只能排序整型数组,而qsort函数非常灵活,能帮助我们排序不同类型的数组元素。
2.1 qsort函数的概念
函数声明:
void qsort (void* base,size_t num ,size_t size ,
int ( * compar)(const void*e1 ,const void*e2) );
//头文件:stdlib.h
void* base 表示数组的起始位置,类型是 void*,size_t num 表示数组的元素个数,类型是unsigned int,size_t size表示的是一个元素的大小,单位是字节,后面的函数指针是指向两元素比较的函数,e1和e2是相比较的两元素的地址。
这里的指针的类型是void*,可以接受任意类型的地址。但是要注意两点:
1,void*指针不能直接解引用,需强制类型转换再解引用。
2,void*指针不能直接加减。
2.2 qsort函数的使用
2.2.1 qsort数组排序
例如一个对整型数组进行升序排列,那么比较函数为:
int cmp_int( const void* e1, const void* e2)
{
return *( int* ) e1 - *( int* )e 2;
}
例如一个对一个字符型数组进行升序排列(按照ASCII值),那么比较函数为:
int cmp_char( const void* e1, const void* e2)
{
return *(char*)e2 - *(char*) e2;
}
//qsor排序整型数组(升序)
#include<stdlib.h>
int cmp_int(const void* e1, const void* e2)
{
return *(int*)e1 - *(int*)e2;
}
int main()
{
int arr[10] = { 5,4,7,3,1,8,9,2,0,6 };
int sz = sizeof(arr) / sizeof(arr[0]);//元素的个数
qsort(arr, sz, sizeof(arr[0]),cmp_int);
int i = 0;
for (i = 0; i < sz; i++) //排完打印
{
printf("%d ", arr[i]);
}
return 0;
}
2.2.2 qsort排序结构体
用qsort排序结构体,可以按照结构体里的任意元素排列。
例如:一个结构体里包含名字,年龄。如果分别按照年龄,名字排,那么比较函数分别为:
int cmp_stu_by_age(const void*e1,const void*e2)
{
return ((struct stu*)e1)->age - ((struct stu*)e2 )->age;//按照年龄排
}
int cmp_stu_by_age(const void*e1,const void*e2)
{
return strcmp(((struct stu*)e1)->name-((struct stu*)e2) ->name);//利用strcmp函数来比较名字
}
//qsort比较结构体
struct stu
{
char name[10];
int age;
char sex[10];
};
int cmp_age(const void* e1, const void* e2)
{
return ((struct stu*)e1)->age - ((struct stu*)e2)->age; //通过年龄比较
}
int main()
{
struct stu s[3] = { {"张三",20,"男"},{"李四", 32,"男"},{"王五",23,"女"} };
int sz = sizeof(s) / sizeof(s[0]);
qsort(s, sz, sizeof(s[0]), cmp_age);
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%s %d %s \n", s[i].name, s[i].age, s[i].sex);
}
return 0;
}
三,模拟实现qsort(采用冒泡的方式)
我们还创建一个无序数组来举例,构造bubble()函数排序,bubble接受的参数与qsort一样。但函数内部实现的过程用冒泡的方式来实现。
#include<stdio.h>
void swap(void* e1, void* e2, int size)
{
int i = 0;
for (i = 0; i < size; i++) //一个字节一个字节的交换。。
{
char tmp = 0;
tmp = *((char*)e1+i); //加i是用来换下个字节来相加
*((char*)e1+i) = *((char*)e2+i);
*((char*)e2+i) = tmp;
}
}
void bubble(void* base, size_t num, size_t size, int (*cmp)(const void*, const void*))
{
int i = 0;
for (i = 0; i < num - 1; i++) //num-1趟
{
int j = 0;
for (j = 0; j < num - i - 1; j++)//一趟的过程
{
if (cmp((char*)base + j * size ,(char*)base + (j+1) * size )>0) //将地址转换为char*然后加上比较类型的大小乘以j,
//用来与下一个类型的数比较
{
swap((char*)base + j * size, (char*)base + (j + 1) * size, size); //将待交换的地址作为函数参数
}
}
}
}
int int_cmp(const void* e1, const void* e2) //整型比较函数
{
return *(int*)e1 - *(int*)e2;
}
int main()
{
int arr[] = { 1,3,5,7,9,2,4,6,8,0 };
//char *arr[] = {"aaaa","dddd","ccccc","bbbb"};
int i = 0;
bubble(arr, sizeof(arr) / sizeof(arr[0]), sizeof(arr[0]), int_cmp); //模拟实现qsort
for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++) //打印
{
printf("%d ",arr[i]);
}
return 0;
}
如果需要比较字符型,结构体,只需要把整型比较函数换成对应的比较函数即可。
这上面的整型比较函数int_cmp()其实也是回调函数。
回调函数是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。
回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
好了,到这里就结束了,希望对大家有所帮助!