冒泡排序与qsort函数详解

简介: 提及到排序,冒泡排序算是一个很基础的排序了。那么冒泡排序到底是什么呢?冒泡排序在什么情况下使用呢?qsort函数又是什么呢?接下来我给大家通过举例来详细解释一下。

 提及到排序,冒泡排序算是一个很基础的排序了。那么冒泡排序到底是什么呢?冒泡排序在什么情况下使用呢?qsort函数又是什么呢?接下来我给大家通过举例来详细解释一下。



引入


  这里给大家一个题目,大家可以简单思考一下:


 例:


int arr[]={3,2,1,4,5,6,8,7,9,0,10};

   将上面数组中的整数排成有序数组(这里的有序是指升序或者降序)。


  当然,一题可能会有多解。在这里我给大家提供两种思路,也就是我们本次要详细解释的冒泡排序与qsort函数。



一、冒泡排序

1、冒泡排序的简单解释


 首先,冒泡排序是一个比较简单的排序算法。它就是将数组中相邻的两个元素进行比较交换,经历过多次遍历数组会到达让一个乱序的数组变成有序数组的一个效果。我们发现,这样的排序就像一块很重的石头沉向底部,或者较轻的小气泡浮向水面,所以我们把这样的排序称之为冒泡排序。冒泡排序的英文名为 Bubble sort,我们也是经常可见的。


2、冒泡排序的详解思路

 我们这里就用引入中例题来做一个详细的解释。我们先例题的数组排成升序来看。我们先看一下第一冒泡排序。如图:


cc3f6e6e167741d2bf898d7b9a128e23.png


 通过上面发现,第一趟排序我们把相邻将较大的数字往后移了一位,也就是每趟排序都会把最大的数字放到最后一位。下一趟排序我们就可以不再和已经排好数字比较。我们这里仔细想一下,如果多次重复这样的操作,会不会将数组排成我们所要的升序呢?答案是肯定会的。那么我们需要几趟这样的排序呢?我们接下来看一个同样是十一个整数,需要这样排序趟数数最多的一个例子。如图:


30f7fd95e62f4ca1b1eb858f2017c529.png


我们通过上图可以相出,对有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函数简单解释


  1. qsort函数时c语言的库函数,所在的头文件是<stdlib.h>
  2. 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函数的应用范围更广,也更加方便。


总结


  1. 冒泡排序可以将乱序的数组排成有序的数组。
  2. 重点掌握的是冒泡排序的思路。
  3. qsort函数应用范围相对冒泡排序比较大。
  4. qsort函数应用起来较为方便。
  5. 重点掌握qsort函数的用法。



 感谢您的观看,希望以上内容对你有所帮助。


相关文章
|
5月前
|
机器学习/深度学习 人工智能 监控
业余AI与专业AI的区别,就在这些评估指标上
如何知道你训练的AI模型是天才还是学渣?本文用轻松幽默的方式带你了解机器学习的各类评估指标,让你不仅能说出模型的好坏,还能找到改进的方向,避免在实际应用中翻车。
|
机器学习/深度学习 数据采集 数据可视化
如何理解数据分析及数据的预处理,分析建模,可视化
如何理解数据分析及数据的预处理,分析建模,可视化
306 0
|
11月前
|
存储 消息中间件 NoSQL
Redis数据结构:List类型全面解析
Redis数据结构——List类型全面解析:存储多个有序的字符串,列表中每个字符串成为元素 Eelement,最多可以存储 2^32-1 个元素。可对列表两端插入(push)和弹出(pop)、获取指定范围的元素列表等,常见命令。 底层数据结构:3.2版本之前,底层采用**压缩链表ZipList**和**双向链表LinkedList**;3.2版本之后,底层数据结构为**快速链表QuickList** 列表是一种比较灵活的数据结构,可以充当栈、队列、阻塞队列,在实际开发中有很多应用场景。
|
12月前
|
NoSQL 安全 Shell
MongoDB 用户管理
10月更文挑战第12天
348 0
|
JavaScript 前端开发 搜索推荐
推荐5款免费、开箱即用的Vue后台管理系统模板
推荐5款免费、开箱即用的Vue后台管理系统模板
678 1
|
JSON 前端开发 JavaScript
|
应用服务中间件
解决方案:IDEA控制台输出Tomcat中文乱码
解决方案:IDEA控制台输出Tomcat中文乱码
499 0
解决方案:IDEA控制台输出Tomcat中文乱码
|
存储 SQL HIVE
金融审批数仓(离线)--DWD层、ADS层
金融审批数仓(离线)--DWD层、ADS层
393 4
|
存储 SQL 运维
Mysql集群方案概述
1: 主从 方案 MysqlReplication 2: 主从可重选举方案 MysqlFabirc 3: 多主多从方案 Mysql Cluster
683 1
|
SpringCloudAlibaba 监控 Dubbo
(十九)、SpringCloud Alibaba Sentinel实现熔断和限流
(十九)、SpringCloud Alibaba Sentinel实现熔断和限流
(十九)、SpringCloud Alibaba Sentinel实现熔断和限流