C语言模拟实现qsort(用冒泡排序的排序方式模拟实现一个通用的排序函数)

简介: C语言模拟实现qsort(用冒泡排序的排序方式模拟实现一个通用的排序函数)

学过C语言的都知道,排序是最基本的操作,而排序的方法又有很多种,直接插入排序,希尔排序,选择排序,堆排序,冒泡排序,快速排序,归并排序,计数排序等等。


      相信很多学过C语言的小伙伴都学过冒泡排序这个经典的排序方法,但是我们一般写的那个冒泡排序是只针对整形数组使用的,如果使用者需要排序一个浮点型的数组或者是一个结构体数组的话,这样的冒泡排序是实现不了的,今天我就给大家分享一个通用的qsort函数。



可以看到,上图中的qsort函数的返回类型是void,有4个参数,第一个参数是待排序的数组的起始地址,第二个参数是数组中元素的数目,第三个参数是数组中每个元素的大小(单位是字节),第四个参数是一个函数指针,由图可知,参数是两个void*类型的指针,为什么是void*而不是(int*)或者(float*)呢?是因为编写这个qsort函数的程序员不知道将来用这个函数的人会排序什么类型的数组,而void*类型的指针就刚好能够接收任意类型的地址,所以用void*指针作为参数是最合适不过的了,这个函数指针的返回类型是int,当第一个元素大于第二个元素的时候,返回一个大于0的整数,相等返回0,第一个小于第二个的时候返回一个小于0的整数。


      具体细节看代码的注释:


void swap(char* buf1, char* buf2, int width)
{
  int i = 0;
    //数组中的每一个数据占width个字节,我们只需要循环width次就能把两个元素的每一个字节都交换,
    //最终两个元素的内容也就交换成功了
  for (i = 0; i < width; i++)
  {
    char tmp = *buf1;
    *buf1 = *buf2;
    *buf2 = tmp;
    buf1++;
    buf2++;
  }
}
void bubble_sort(void* base, int sz, int width, int (*cmp)(const void* e1,const void* e2))
{
    //这里是典型的冒泡排序的方法
  int i = 0;
  for (i = 0; i < sz-1; i++)
  {
    int j = 0;
    for (j = 0; j < sz - 1 - i; j++)
    {
            //这里的cmp函数就是就是比较相邻的两个元素的大小,如果返回值大于0,则证明前一个元素
            //比后一个元素大,则需要交换这两个元素。由于这里的base指针的类型是void*,所以我们
            //首先需要将它强制类型转换成char*类型的指针,那为什么是转换成char*而不是int*, 
            //double*呢?其实很简单,你试想一下,我们比较完了两个相邻的元素之后是不是需要
            //拿后一个和这两个元素中大的元素进行比较大小,但是大家别忘了,指针类型的大小可是决 
            //定了你指针加1跳过几个字节的啊,整形指针加1跳过一个整形,字符指针加1跳过一个字节
            //但是我们并不知道将来这个函数会被用来排序什么类型的数据的啊,但是无论是数目类型的 
            //数据,它的大小都至少为1个字节吧,所以转换成(char*)类型是最合理的。而参数中的宽 
            //度又正好能让我们找到下一个元素,只需要再起始地址加上宽度*j就能找到下一个元素了
            //所以if语句里面的判断条件应该这样写
      if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
      {
        swap((char*)base + j * width, (char*)base + (j + 1) * width,width);
      }
    }
  }
}
int cmp_int(const void* e1, const void* e2)
{
    //由于e1和e2都是void*类型的指针,所以要先把它们强制类型转换成(int*)类型才能进行解引用
    //操作,由于我们排的是升序,所以前一个元素减去后一个元素刚好反映了两个元素的大小关系,
    //而这个函数如果前一个元素比后一个大就返回整数,相等返回0,否则小于返回负数,所以我们可以直            
    //接返回两个数的相减的结果。
  return *((int*)e1) - *((int*)e2);
}
void test1(void)
{
  int arr[] = { 9,8,7,6,5,4,3,2,1,0 };//排序一个整形数组
  int sz = sizeof(arr) / sizeof(arr[0]);//计算数组大小
    //调用bubble_sort函数排序,第一个参数是待排序的数组起始位置,接着是数组的元素个数,接着是
    //数组每个元素的大小(单位是字节),接着是一个函数指针,这个函数的具体内容是待排序数组的排    
    //序方法,比如整形数组可以直接用大于小于或者等于比较大小,而像结构体数组这种单个元素是复杂 
    //的对象的时候则需要规定一个比较方法,例如是通过年龄比较还是通过名字比较,这个函数就是实现
    //这个数组里元素的比较方法的
  bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);  
    int i = 0;
  for (i = 0; i < sz; i++)
  {
    printf("%d ", arr[i]);//打印排好序的数组各个元素
  }
}
struct Stu
{
  char name[20];
  int age;
};
int cmp_stu_by_name(const void* e1, const void* e2)
{
    //通过姓名对结构体进行排序,需要用到strcmp函数,依然是返回1,0,-1
  return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
void test2(void)
{
  struct Stu s[] = { {"zhangsan",18},{"wangwu",17},{"lisi",20} };
  int sz = sizeof(s) / sizeof(s[0]);
  bubble_sort(s, sz, sizeof(s[0]), cmp_stu_by_name);
}
int cmp_stu_by_age(const void* e1, const void* e2)
{
  return (((struct Stu*)e1)->age - ((struct Stu*)e2)->age);
}
void test3(void)
{
  struct Stu s[] = { {"zhangsan",18},{"wangwu",17},{"lisi",20} };
  int sz = sizeof(s) / sizeof(s[0]);
  bubble_sort(s, sz, sizeof(s[0]), cmp_stu_by_age);
}
int main()
{
  test1();
  test2();
  test3();
  return 0;
}


  以上就是一个通用的快速排序qsort函数的实现,你们学会了吗?大家有兴趣可以用其他类型的数组进行排序测试一下。

相关文章
|
12天前
|
C语言
c语言调用的函数的声明
被调用的函数的声明: 一个函数调用另一个函数需具备的条件: 首先被调用的函数必须是已经存在的函数,即头文件中存在或已经定义过; 如果使用库函数,一般应该在本文件开头用#include命令将调用有关库函数时在所需要用到的信息“包含”到本文件中。.h文件是头文件所用的后缀。 如果使用用户自己定义的函数,而且该函数与使用它的函数在同一个文件中,一般还应该在主调函数中对被调用的函数做声明。 如果被调用的函数定义出现在主调函数之前可以不必声明。 如果已在所有函数定义之前,在函数的外部已做了函数声明,则在各个主调函数中不必多所调用的函数在做声明
27 6
|
14天前
|
搜索推荐 算法 C语言
【排序算法】八大排序(上)(c语言实现)(附源码)
本文介绍了四种常见的排序算法:冒泡排序、选择排序、插入排序和希尔排序。通过具体的代码实现和测试数据,详细解释了每种算法的工作原理和性能特点。冒泡排序通过不断交换相邻元素来排序,选择排序通过选择最小元素进行交换,插入排序通过逐步插入元素到已排序部分,而希尔排序则是插入排序的改进版,通过预排序使数据更接近有序,从而提高效率。文章最后总结了这四种算法的空间和时间复杂度,以及它们的稳定性。
61 8
|
14天前
|
搜索推荐 算法 C语言
【排序算法】八大排序(下)(c语言实现)(附源码)
本文继续学习并实现了八大排序算法中的后四种:堆排序、快速排序、归并排序和计数排序。详细介绍了每种排序算法的原理、步骤和代码实现,并通过测试数据展示了它们的性能表现。堆排序利用堆的特性进行排序,快速排序通过递归和多种划分方法实现高效排序,归并排序通过分治法将问题分解后再合并,计数排序则通过统计每个元素的出现次数实现非比较排序。最后,文章还对比了这些排序算法在处理一百万个整形数据时的运行时间,帮助读者了解不同算法的优劣。
51 7
|
25天前
|
存储 算法 程序员
C语言:库函数
C语言的库函数是预定义的函数,用于执行常见的编程任务,如输入输出、字符串处理、数学运算等。使用库函数可以简化编程工作,提高开发效率。C标准库提供了丰富的函数,满足各种需求。
|
30天前
|
机器学习/深度学习 C语言
【c语言】一篇文章搞懂函数递归
本文详细介绍了函数递归的概念、思想及其限制条件,并通过求阶乘、打印整数每一位和求斐波那契数等实例,展示了递归的应用。递归的核心在于将大问题分解为小问题,但需注意递归可能导致效率低下和栈溢出的问题。文章最后总结了递归的优缺点,提醒读者在实际编程中合理使用递归。
60 7
|
28天前
|
存储 C语言
【c语言】字符串函数和内存函数
本文介绍了C语言中常用的字符串函数和内存函数,包括`strlen`、`strcpy`、`strcat`、`strcmp`、`strstr`、`strncpy`、`strncat`、`strncmp`、`strtok`、`memcpy`、`memmove`和`memset`等函数的使用方法及模拟实现。文章详细讲解了每个函数的功能、参数、返回值,并提供了具体的代码示例,帮助读者更好地理解和掌握这些函数的应用。
23 0
|
28天前
|
C语言
【c语言】qsort函数及泛型冒泡排序的模拟实现
本文介绍了C语言中的`qsort`函数及其背后的回调函数概念。`qsort`函数用于对任意类型的数据进行排序,其核心在于通过函数指针调用用户自定义的比较函数。文章还详细讲解了如何实现一个泛型冒泡排序,包括比较函数、交换函数和排序函数的编写,并展示了完整的代码示例。最后,通过实际运行验证了排序的正确性,展示了泛型编程的优势。
20 0
|
搜索推荐 C语言
【C语言】使用回调函数通过冒泡排序模拟实现qsort函数
【C语言】使用回调函数通过冒泡排序模拟实现qsort函数
【C语言】使用回调函数通过冒泡排序模拟实现qsort函数
|
1月前
|
C语言 C++
C语言 之 内存函数
C语言 之 内存函数
35 3
|
1月前
|
存储 缓存 C语言
【c语言】简单的算术操作符、输入输出函数
本文介绍了C语言中的算术操作符、赋值操作符、单目操作符以及输入输出函数 `printf` 和 `scanf` 的基本用法。算术操作符包括加、减、乘、除和求余,其中除法和求余运算有特殊规则。赋值操作符用于给变量赋值,并支持复合赋值。单目操作符包括自增自减、正负号和强制类型转换。输入输出函数 `printf` 和 `scanf` 用于格式化输入和输出,支持多种占位符和格式控制。通过示例代码详细解释了这些操作符和函数的使用方法。
36 10