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函数的实现,你们学会了吗?大家有兴趣可以用其他类型的数组进行排序测试一下。

相关文章
|
2天前
|
存储 C语言
`scanf`是C语言中用于按格式读取标准输入的函数
`scanf`是C语言中用于按格式读取标准输入的函数,通过格式字符串解析输入并存入指定变量。需注意输入格式严格匹配,并建议检查返回值以确保读取成功,提升程序健壮性。
113 0
|
2月前
|
安全 C语言
C语言中的字符、字符串及内存操作函数详细讲解
通过这些函数的正确使用,可以有效管理字符串和内存操作,它们是C语言编程中不可或缺的工具。
238 15
|
7月前
|
人工智能 Java 程序员
一文彻底搞清楚C语言的函数
本文介绍C语言函数:函数是程序模块化的工具,由函数头和函数体组成,涵盖定义、调用、参数传递及声明等内容。值传递确保实参不受影响,函数声明增强代码可读性。君志所向,一往无前!
182 1
一文彻底搞清楚C语言的函数
|
8月前
|
C语言
【C语言程序设计——函数】亲密数判定(头歌实践教学平台习题)【合集】
本文介绍了通过编程实现打印3000以内的全部亲密数的任务。主要内容包括: 1. **任务描述**:实现函数打印3000以内的全部亲密数。 2. **相关知识**: - 循环控制和跳转语句(for、while循环,break、continue语句)的使用。 - 亲密数的概念及历史背景。 - 判断亲密数的方法:计算数A的因子和存于B,再计算B的因子和存于sum,最后比较sum与A是否相等。 3. **编程要求**:根据提示在指定区域内补充代码。 4. **测试说明**:平台对代码进行测试,预期输出如220和284是一组亲密数。 5. **通关代码**:提供了完整的C语言代码实现
148 24
|
8月前
|
存储 C语言
【C语言程序设计——函数】递归求斐波那契数列的前n项(头歌实践教学平台习题)【合集】
本关任务是编写递归函数求斐波那契数列的前n项。主要内容包括: 1. **递归的概念**:递归是一种函数直接或间接调用自身的编程技巧,通过“俄罗斯套娃”的方式解决问题。 2. **边界条件的确定**:边界条件是递归停止的条件,确保递归不会无限进行。例如,计算阶乘时,当n为0或1时返回1。 3. **循环控制与跳转语句**:介绍`for`、`while`循环及`break`、`continue`语句的使用方法。 编程要求是在右侧编辑器Begin--End之间补充代码,测试输入分别为3和5,预期输出为斐波那契数列的前几项。通关代码已给出,需确保正确实现递归逻辑并处理好边界条件,以避免栈溢出或结果
365 16
|
8月前
|
存储 编译器 C语言
【C语言程序设计——函数】分数数列求和2(头歌实践教学平台习题)【合集】
函数首部:按照 C 语言语法,函数的定义首部表明这是一个自定义函数,函数名为fun,它接收一个整型参数n,用于指定要求阶乘的那个数,并且函数的返回值类型为float(在实际中如果阶乘结果数值较大,用float可能会有精度损失,也可以考虑使用double等更合适的数据类型,这里以float为例)。例如:// 函数体代码将放在这里函数体内部变量定义:在函数体中,首先需要定义一些变量来辅助完成阶乘的计算。比如需要定义一个变量(通常为float或double类型,这里假设用float。
199 3
|
8月前
|
存储 算法 安全
【C语言程序设计——函数】分数数列求和1(头歌实践教学平台习题)【合集】
if 语句是最基础的形式,当条件为真时执行其内部的语句块;switch 语句则适用于针对一个表达式的多个固定值进行判断,根据表达式的值与各个 case 后的常量值匹配情况,执行相应 case 分支下的语句,直到遇到 break 语句跳出 switch 结构,若没有匹配值则执行 default 分支(可选)。例如,在判断一个数是否大于 10 的场景中,条件表达式为 “num> 10”,这里的 “num” 是程序中的变量,通过比较其值与 10 的大小关系来确定条件的真假。常量的值必须是唯一的,且在同一个。
171 2
|
8月前
|
存储 算法 安全
【C语言程序设计——选择结构程序设计】按从小到大排序三个数(头歌实践教学平台习题)【合集】
本任务要求从键盘输入三个数,并按从小到大的顺序排序后输出。主要内容包括: - **任务描述**:实现三个数的排序并输出。 - **编程要求**:根据提示在编辑器中补充代码。 - **相关知识**: - 选择结构(if、if-else、switch) - 主要语句类型(条件语句) - 比较操作与交换操作 - **测试说明**:提供两组测试数据及预期输出。 - **通关代码**:完整代码示例。 - **测试结果**:展示测试通过的结果。 通过本任务,你将掌握基本的选择结构和排序算法的应用。祝你成功!
114 4
|
8月前
|
存储 编译器 C语言
【C语言程序设计——函数】回文数判定(头歌实践教学平台习题)【合集】
算术运算于 C 语言仿若精密 “齿轮组”,驱动着数值处理流程。编写函数求区间[100,500]中所有的回文数,要求每行打印10个数。根据提示在右侧编辑器Begin--End之间的区域内补充必要的代码。如果操作数是浮点数,在 C 语言中是不允许直接进行。的结果是 -1,因为 -7 除以 3 商为 -2,余数为 -1;注意:每一个数据输出格式为 printf("%4d", i);的结果是 1,因为 7 除以 -3 商为 -2,余数为 1。取余运算要求两个操作数必须是整数类型,包括。开始你的任务吧,祝你成功!
143 1