qsort 函数的使用及其模拟实现

简介: qsort 函数的使用及其模拟实现

qsort 函数

函数功能

qsort 是C语言中基于快速排序思想的一种排序函数,与我们之前学过的冒泡排序不同,qsort 可以排序任意类型的数据(整形、浮点型、数组、结构体等等),同时,qsort 函数也是函数指针中回调函数应用的一个经典案例 。

函数参数

void qsort( void *base,
           size_t num, 
           size_t width, 
           int (__cdecl *compare )(const void *elem1, const void *elem2 ) );
# void* base:要排序数据的起始地址,把指针类型定义为void*,让qsort函数可以接收任何类型数据的地址;
# size_t num:要排序的元素个数;
# size_t width:每个元素的大小;
# __cdecl:函数调用约定;
# int (*compare)(const void *elem1, const void *elem2 )):函数指针,指向用于排序的函数

函数指针

假设我这里有一个名为 struct Stu 的结构体,里面有 name、age、height 三个成员变量,现在我们要调用 qsort 函数对多个这样的结构体变量进行排序,那么这里就会出现一个问题;


struct Stu 内部的排序依据有三个,分别是 name、age 和 height,我们即函数的调用者肯定是清楚我们想要以哪种依据来排序的,但是qsort 函数的实现者显然并不知道;


所以 qsort 函数中第四个参数是一个函数指针,该函数指针指向一个排序函数,该函数需要由 qsort 的调用者来提供,用于指定两个数据以何种方式进行比较。

int (*compare)(const void *elem1, const void *elem2 ));
# const void *elem1:用于比较的第一个数据;
# const void *elem2:用于比较的第二个数据;

排序函数的返回值

-返回值 -对应情况
= 0 两个数据相等
> 0 第一个数据大于第二个数据
< 0 第一个数据小于第二个数据

函数使用

我们以上面提到的 struct Stu 结构体进行举例;

以 name 为依据进行排序:

#include <stdio.h>
#include <stdlib.h>  //qsort 对应头文件
#include <string.h>  //strcmp 对应头文件
struct Stu
{
  char name[20];
  int age;
  int height;
};
int sort_by_name(const void* e1, const void* e2)  //排序函数
{
  //由于e1 e2 是void*类型,不能直接解引用,所以我们需要先将其强转为 struct Stu* 类型,然后再找出其中的 name 进行比较
  //由于strcmp函数和排序函数的返回值相同,且name是字符串,所以这里我们直接调用strcmp函数
  return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
int main()
{
  struct Stu stu[3] = { {"zhangsan", 23, 185}, {"lisi", 20, 180}, {"wangwu", 25, 186} };
  qsort(stu, 3, sizeof(struct Stu), sort_by_name);
  int i = 0;
  for (i = 0; i < 3; i++)
  {
    printf("姓名:%s\t年龄:%d\t身高:%d\n", stu[i].name, stu[i].age, stu[i].height);
  }
  return 0;
}

2020062310470442.png

以 age 为依据进行比较:

#include <stdio.h>
#include <stdlib.h>  //qsort 对应头文件
struct Stu
{
  char name[20];
  int age;
  int height;
};
int sort_by_age(const void* e1, const void* e2)  //排序函数
{
    //由于e1 e2 是void*类型,不能直接解引用,所以我们需要先将其强转为 struct Stu* 类型,然后再找出其中的 age 进行比较
  //根据排序函数的返回值要求,我们直接返回二者的差值即可
  return (((struct Stu*)e1)->age - ((struct Stu*)e2)->age);
}
int main()
{
  struct Stu stu[3] = { {"zhangsan", 23, 185}, {"lisi", 20, 180}, {"wangwu", 25, 186} };
  qsort(stu, 3, sizeof(struct Stu), sort_by_age);
  int i = 0;
  for (i = 0; i < 3; i++)
  {
    printf("姓名:%s\t年龄:%d\t身高:%d\n", stu[i].name, stu[i].age, stu[i].height);
  }
  return 0;
}

2020062310470442.png

以 height 为依据进行比较:

#include <stdio.h>
#include <stdlib.h>  //qsort 对应头文件
struct Stu
{
  char name[20];
  int age;
  int height;
};
int sort_by_height(const void* e1, const void* e2)  //排序函数
{
    //由于e1 e2 是void*类型,不能直接解引用,所以我们需要先将其强转为 struct Stu* 类型,然后再找出其中的height进行比较
  //根据排序函数的返回值要求,我们直接返回二者的差值即可
  return (((struct Stu*)e1)->height - ((struct Stu*)e2)->height);
}
int main()
{
  struct Stu stu[3] = { {"zhangsan", 23, 185}, {"lisi", 20, 180}, {"wangwu", 25, 186} };
  qsort(stu, 3, sizeof(struct Stu), sort_by_height);
  int i = 0;
  for (i = 0; i < 3; i++)
  {
    printf("姓名:%s\t年龄:%d\t身高:%d\n", stu[i].name, stu[i].age, stu[i].height);
  }
  return 0;
}

2020062310470442.png

qsort 函数的模拟实现

我们之前学习了冒泡排序,我们知道,冒泡排序只能排序整形的数据;今天我们就用快速排序的思想来对冒泡排序进行改造,让它能够达到和 qsort 函数同样的效果,可以排序任意类型的数据。

冒泡排序

首先我们先来写一个普通的冒泡排序:

void bubble_sort(int arr[], int n)
{
  int i = 0;
  int j = 0;
  for (i = 0; i < n - 1; i++)
  {
    for (j = 0; j < n - i - 1; j++)
    {
      if (arr[j] > arr[j + 1])
      {
        int tmp = arr[j];
        arr[j] = arr[j + 1];
        arr[j + 1] = tmp;
      }
    }
  }
}
int main()
{
  int arr[] = { 1,2,4,5,8,3,6,7,9 };
  int sz = sizeof(arr) / sizeof(arr[0]);
  bubble_sort(arr, sz);  //冒泡排序
  int i = 0;
  for (i = 0; i < sz; i++)
  {
    printf("%d ", arr[i]);
  }
  return 0;
}

2020062310470442.png

现在我们来对冒泡排序进行改造:

首先是参数的设置,为了达到和 qsort 函数同样的效果,我们这里参数和 qsort 设置为一样;然后是代具体实现,冒泡排序的整体框架我们不用改变,要改变的地方只是元素进行比较和交换的方法。

void Swap(char* buf1, char* buf2, size_t width)
{
  int i = 0;
  for (i = 0; i < width; i++)  //将两个元素每一对字节的内容进行交换
  {
    char tmp = *buf1;
    *buf1 = *buf2;
    *buf2 = tmp;
    buf1++;
    buf2++;
  }
}
void bubble_sort(void* base, size_t num, size_t width, int (*cmp)(const void* e1, const void* e2))
{
  int i = 0;
  int j = 0;
  for (i = 0; i < num - 1; i++)
  {
    for (j = 0; j < num - i - 1; j++)
    {
      //由于base是void*的,所以不能直接对其进行+-整数的操作
      //同时又为了能够操作任意类型的数据,我们把base强转为最小数据类型的大小:char*
      //回调函数:使用排序函数的返回值判断是否要进行元素的交换
      if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
      {
        //如果前一个元素>后一个元素,就将两个元素的数据进行交换
        Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
      }
    }
  }
}

现在我们用改造后的冒泡排序来对我们上面的结构体进行排序,检验代码的正确性:

struct Stu
{
  char name[20];
  int age;
  int height;
};
int sort_by_name(const void* e1, const void* e2)  //排序函数:按姓名
{
  return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
int sort_by_age(const void* e1, const void* e2)  //排序函数:按年龄
{
  return (((struct Stu*)e1)->age - ((struct Stu*)e2)->age);
}
int sort_by_height(const void* e1, const void* e2)  //排序函数:按身高
{
  return (((struct Stu*)e1)->height - ((struct Stu*)e2)->height);
}
int main()
{
  struct Stu stu[3] = { {"zhangsan", 23, 185}, {"lisi", 20, 180}, {"wangwu", 25, 186} };
  int i = 0;
  bubble_sort(stu, 3, sizeof(struct Stu), sort_by_name);
  printf("以姓名排序:\n");
  for (i = 0; i < 3; i++)
  {
    printf("姓名:%s\t年龄:%d\t身高:%d\n", stu[i].name, stu[i].age, stu[i].height);
  }
  printf("----------------------\n");
  bubble_sort(stu, 3, sizeof(struct Stu), sort_by_age);
  printf("以年龄排序:\n");
  for (i = 0; i < 3; i++)
  {
    printf("姓名:%s\t年龄:%d\t身高:%d\n", stu[i].name, stu[i].age, stu[i].height);
  }
  printf("----------------------\n");
  bubble_sort(stu, 3, sizeof(struct Stu), sort_by_height);
  printf("以体重排序:\n");
  for (i = 0; i < 3; i++)
  {
    printf("姓名:%s\t年龄:%d\t身高:%d\n", stu[i].name, stu[i].age, stu[i].height);
  }
  return 0;
}

2020062310470442.png

我们上面只是用冒泡排序来模拟实现了 qsort 函数的功能,并不是说 qsort 函数的内部也是用冒泡排序实现的,这样做明显有些得不偿失,因为冒泡排序的时间复杂度是比较高的;但是它们都能达到一样的效果,并且都是基于快速排序的思想来设计的。


相关文章
|
C语言
strlen函数【详解+模拟实现】
strlen函数【详解+模拟实现】
|
6月前
|
编译器 C语言
库函数qsort的使用及利用冒泡排序模拟实现qsort
库函数qsort的使用及利用冒泡排序模拟实现qsort
|
1月前
|
C++
指针中的回调函数与qsort的深度理解与模拟
本文详细介绍了回调函数的概念及其在计算器简化中的应用,以及C++标准库函数qsort的原理和使用示例,包括冒泡排序的模拟实现。
19 1
|
6月前
|
搜索推荐 C语言
冒泡排序模拟实现qsort()函数
冒泡排序模拟实现qsort()函数
40 0
qsort函数和模拟实现qsort函数
qsort函数和模拟实现qsort函数
|
11月前
|
程序员
qsort函数的模拟实现
qsort函数的模拟实现
52 1
|
算法 程序员 C语言
【进阶C语言】排序函数(qsort)与模拟实现(回调函数的实例)
回调函数为C语言重要知识点,以函数指针为主要知识;下面介绍回调函数的定义、回调函数的库函数举例即库函数模拟实现。
52 0
【进阶C语言】排序函数(qsort)与模拟实现(回调函数的实例)
|
存储 算法 测试技术
深入解析 qsort 函数(下),用冒泡排序模拟实现 qsort 函数
深入解析 qsort 函数(下),用冒泡排序模拟实现 qsort 函数
47 0
|
C语言 C++
深入理解回调函数qsort:从入门到模拟实现(下)
深入理解回调函数qsort:从入门到模拟实现(下)
45 0
|
搜索推荐 C语言
深入理解回调函数qsort:从入门到模拟实现(上)
深入理解回调函数qsort:从入门到模拟实现(上)
85 0