提及到排序,冒泡排序算是一个很基础的排序了。那么冒泡排序到底是什么呢?冒泡排序在什么情况下使用呢?qsort函数又是什么呢?接下来我给大家通过举例来详细解释一下。
引入
这里给大家一个题目,大家可以简单思考一下:
例:
int arr[]={3,2,1,4,5,6,8,7,9,0,10};
将上面数组中的整数排成有序数组(这里的有序是指升序或者降序)。
当然,一题可能会有多解。在这里我给大家提供两种思路,也就是我们本次要详细解释的冒泡排序与qsort函数。
一、冒泡排序
1、冒泡排序的简单解释
首先,冒泡排序是一个比较简单的排序算法。它就是将数组中相邻的两个元素进行比较交换,经历过多次遍历数组会到达让一个乱序的数组变成有序数组的一个效果。我们发现,这样的排序就像一块很重的石头沉向底部,或者较轻的小气泡浮向水面,所以我们把这样的排序称之为冒泡排序。冒泡排序的英文名为 Bubble sort,我们也是经常可见的。
2、冒泡排序的详解思路
我们这里就用引入中例题来做一个详细的解释。我们先例题的数组排成升序来看。我们先看一下第一冒泡排序。如图:
通过上面发现,第一趟排序我们把相邻将较大的数字往后移了一位,也就是每趟排序都会把最大的数字放到最后一位。下一趟排序我们就可以不再和已经排好数字比较。我们这里仔细想一下,如果多次重复这样的操作,会不会将数组排成我们所要的升序呢?答案是肯定会的。那么我们需要几趟这样的排序呢?我们接下来看一个同样是十一个整数,需要这样排序趟数数最多的一个例子。如图:
我们通过上图可以相出,对有n个数字的整形数组排序,我们最坏的情况是需要排n-1趟;这样就是整个冒泡排序的思路了。接下来我们可以结合着代码一起理解一下。
#include<stdio.h> int main() { int arr[] = { 3,2,1,4,5,6,8,7,9,0,10}; int i = 0; int sz = sizeof(arr) / sizeof(arr[0]); //对arr进行排序,拍成升序 //确定冒泡排序的趟数 for (i = 0; i < n - 1; i++) { int j = 0; int flag = 1; //假设这一趟排序已经是完全有序 //每一趟冒泡排序 for (j = 0; j < n - 1 - i; j++) //每次需要比较的整数的个数 { if (arr[j] > arr[j + 1]) { int num = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = num; flag = 0;//这趟排顺序不完全有序 } } if (flag == 1) { break; } for (i = 0; i < sz; i++) { printf("arr[%d]=%d\n", i, arr[i]); } return 0; }
这里我再解释一下,为什么要设置一个变量flag。在大多情况下,我们都是不用到排n-1趟就已经是有序的了。所以我们这里来设置一个变量flag来判断是否已经有序,来提高代码的效率。当有一趟排序不再交换数字时,就说明这组数字已经是有序的数组了。也就是不用再循环比较了。当这一趟排序仍有交换数字时,说明还不是有序数组,还需要循环进行下一趟比较。
到这里你应该对冒泡排序已经有了一个很好的了解。但是我们发现,冒泡排序好像只是能排整形,那么其他类型的怎么排序呢?c语言这时给我们提供了qsort函数,我们再来了解一下qsort函数。
二、qsort()函数
1、qsort函数简单解释
- qsort函数时c语言的库函数,所在的头文件是<stdlib.h>
- qsort函数是一个排序函数,可以简单理解为冒泡排序的升级版,可以排序任意类型。
2、qsort函数用法及详解
qsort()函数的底层实现是用快排实现的,在这里我们只需要掌握其用法即可。
我们先来看一下标准的qsort函数的用法:void qsort( void *base, size_t num, size_t width, int (__cdecl *compare )(const void *elem1, const void *elem2 ) );
从上面我们可以看出,qsort的返回值类型是void型,有四个参数。我们再看一下四个参数分别的含义:
第一个参数意义:数组的首元素地址;
第二个参数意义:数组中元素的个数;
第三个参数意义:数组中每个元素的大小(单位是字节);
第四个参数意义:比较函数(这里的比较函数是要自己写的,有固定格式);
我们通过qsort函数来对上面的例题进行排序,代码如下:
#include<stdio.h> #include<stdlib.h> int cmp_int(const void*e1,const void*e2) { return *(int*)e1-*(int*)e2; } int main() { int arr[] = { 3,2,1,4,5,6,8,7,9,0,10 }; int sz = sizeof(arr) / sizeof(arr[0]); //计算数组元素的个数 qsort(arr,sz,sizeof(arr[0]),cmp_int); for (i = 0; i < sz; i++) { printf("arr[%d]=%d\n", i, arr[i]); } return 0; }
通过上面的代码我们先来熟悉一下我们自定义的比较函数。这里的比较函数两个参数是固定的,返回类型为int型。我们只要要做的是把两个参数转换为你所需要比较类型的指针,然后再解引用即可。这里我给大家多举几个例子,方便理解。如下:
#include<stdio.h> #include<stdlib.h> #include<string.h> int cmp_int(const void*e1,const void*e2) { return *(int*)e1-*(int*)e2; } void test1() { int arr[]={9,8,7,6,5,4,3,2,1,0}; 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]); } } int cmp_float(const void* e1,const void* e2) { return (int)(*(float*)e1-*(float*)e2); } void test2() { float ar[]={9.0,8.0,7.0,6.0,5.0,4.0,3.0,2.0,1.0,0.0}; int sz=sizeof(ar)/sizeof(ar[0]); qsort(ar,sz,sizeof(ar[0]),cmp_float); int i=0; for(i=0;i<sz;i++) { printf("%f ",ar[i]); } } struct stu { char name[20]; int age; }; int cmp_by_age(const void*e1,const void*e2) { return ((struct stu*)e1)->age-((struct stu*)e2)->age; } void test3() { struct stu s[3]={{"zhangsan",20},{"lisi",25},{"wangwu",30}}; int sz=sizeof(s)/sizeof(s[0]); qsort(s,sz,sizeof(s[0]),cmp_by_age); int i; for(i=0;i<sz;i++) { printf("%d ",s[i].age); } } int cmp_by_name(const void*e1,const void*e2) { return strcmp(((struct stu*)e1)->name,((struct stu*)e2)->name); //这里引用了strcmp()函数,头文件为string.h,这个函数的作用是将字符串进行比较大小,比较的是字符串首个字母,首个字母转换为ASCLL码值进行比较。 } void test4() { struct stu s[3]={{"zhangsan",20},{"lisi",25},{"wangwu",30}}; int sz=sizeof(s)/sizeof(s[0]); qsort(s,sz,sizeof(s[0]),cmp_by_name); int i; for(i=0;i<sz;i++) { printf("%s ",s[i].name); } } int main() { //test1(); //test2(); //test3(); test4(); return 0; }
通过测试,我们发现qsort函数默认的排序是升序的。当然我们想要降序的时候,只需要将比较函数中的e1-e2改成e2-e1即可;
我们了解完qsot函数后发现:相对冒泡排序,qsort函数的应用范围更广,也更加方便。
总结
- 冒泡排序可以将乱序的数组排成有序的数组。
- 重点掌握的是冒泡排序的思路。
- qsort函数应用范围相对冒泡排序比较大。
- qsort函数应用起来较为方便。
- 重点掌握qsort函数的用法。
感谢您的观看,希望以上内容对你有所帮助。