C语言之qsort()函数的模拟实现

C语言之qsort()函数的模拟实现

文章目录

  • C语言之qsort()函数的模拟实现
    • 1. 简介
    • 2. 冒泡排序
    • 3. 对冒泡排序进行改造
    • 4. 改造部分
      • 4.1 保留部分的冒泡排序
      • 4.2 比较部分
      • 4.3 交换部分
    • 5. bubble_sort2完整代码
    • 6. 使用bubble_sort2来排序整型数组
    • 7. 使用bubble_sort2来排序结构体数组
      • 7.1 按名字来排序结构体数组
      • 7.2 按年龄来排序结构体数组

1. 简介

qsort()函数全称为Quicksort,因为底层使用的是快速排序,对初学者来说只学过冒泡排序,使用我们使用冒泡排序来实现下qsort()函数,qsort()函数(冒泡排序版)

不知道qsort函数怎么使用的可以看看这篇qsort函数

2. 冒泡排序

既然要使用冒泡排序改模拟实现qsort,那得知道什么是冒泡排序
代码如下:

#include <stdio.h>void bubble_sort(int arr[], int sz)
{int i = 0;//趟数for (i = 0; i < sz - 1; i++){//一趟冒泡排序int j = 0;for (j = 0; j < sz - 1 - i; 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,4,7,2,5,8,3,10,6,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;
}

3. 对冒泡排序进行改造

void bubble_sort(int arr[], int sz);
void qsort (void* base, size_t num, size_t size,            int (*compar)(const void*,const void*));

先来看看冒泡排序和qsort函数的函数声明
要想用冒泡排序模拟实现qsort函数,首先函数的形参要一致

void bubble_sort2(void* base,size_t sz,size_t width,  int (*cmp)(const void* p1,const void* p2));

仿照着qsort的形参来写

  1. void*:由于我们不知道要排序什么数组,所以我们使用void*来接收
  2. sz:为数组中元素的个数
  3. width:为数组中一个元素的大小(单位为字节)
  4. int (cmp)(const void p1,const void* p2):是函数指针
    这个函数指针指向的函数是用来比较两个元素的大小
    p1指向一个元素,p2也指向一个元素

函数的声明部分写好了,就得改造函数内部了
先来看看冒泡排序中的代码
在这里插入图片描述

  1. 首先函数的形参得更改吧 改为上边的函数声明
  2. 趟数取决于数组中元素个数使用不需要修改
  3. 冒泡排序只能排序整型,所以一趟冒泡排序中的比较部分需要修改
  4. 交换两个元素的位置也需要修改

4. 改造部分

4.1 保留部分的冒泡排序

先将冒泡排序还能使用的部分保留下来

void bubble_sort2(void* base, size_t sz, size_t width, int (*cmp)(const void* p1, const void* p2))
{int i = 0;//趟数for (i = 0; i < sz - 1; i++){int j = 0;//一趟冒泡排序for (j = 0; j < sz - 1 - i; j++){//比较两个元素的大小if (){//Swap用于交换两个元素的位置Swap();}}}
}int main()
{int arr[] = { 1,4,7,2,5,8,3,10,6,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;
}

其中的比较部分和交换部分需要我们自己来实现

4.2 比较部分

cmp((char*)base + j * width, (char*)base + (j + 1) * width)
  1. 由于我们得到的数组名,也就是首元素的地址,不知道第二个元素的地址
    我们可以通过指针偏移来找到第二个元素

  2. 我们得到的是void类型的数据,我们需要将其强制转换成char类型的数据,以便于我们进行指针偏移

  3. 不知道数组的类型,我们只知道一个元素的大小,我们可以通过(char*)base + j + width的方式来找到一个元素的地址,(char*)base + (j+1) + width的方式来找到后一个元素的地址
    用来模拟arr[j] 和 arr[j+1]
    在这里插入图片描述

  4. 为什么其他类型的指针不行呢,我来举个例子:

假设用来排序double类型的数据,也就是每个元素是8字节 width = 8
(char*)base + j * width 和 (char*)base + (j+1) * width
当j等于0时,char类型的指针会偏移8个字节,找到第二个元素
如果换成其他类型的指针 int* 在这个情况下就不能使用了,只有char*类型的指针偏移是1个字节,适用于任意类型的排序

4.3 交换部分

void Swap(char* p1, char* p2,size_t width)
{int i = 0;for (i = 0; i < width; i++){char tmp = *p1;*p1 = *p2;*p2 = tmp;p1++;p2++;}
}
  1. 由于在比较部分已经将数据强制转换成char类型,所以Swap函数的形参就可以使用char来接收
  2. 由于不知道接收的是什么类型的数据,但是我们知道每个元素的大小,我们可以通过一个字节一个字节进行交换,和冒泡排序一样定义一个临时变量来交换两个元素,交换完一个字节的内容之后,p1++ p2++来找到第二个字节的内容,直到交换完成

5. bubble_sort2完整代码

void Swap(char* p1, char* p2,size_t width)
{int i = 0;for (i = 0; i < width; i++){char tmp = *p1;*p1 = *p2;*p2 = tmp;p1++;p2++;}
}
void bubble_sort2(void* base, size_t sz, size_t width, int (*cmp)(const void* p1, const void* p2))
{int i = 0;//趟数for (i = 0; i < sz - 1; i++){int j = 0;//一趟冒泡排序for (j = 0; j < sz - 1 - i; j++){//比较两个元素的大小if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0){//Swap用于交换两个元素的位置Swap((char*)base + j * width, (char*)base + (j + 1) * width,width);}}}
}

6. 使用bubble_sort2来排序整型数组

和使用qsort函数一样,第四个形象需要根据需要排序的数据类型来编写函数,然后将其传给qsort函数,整型数据的比较只需要两个元素作差就好了
代码如下:

int cmp_int(const void* p1, const void* p2)
{return *(int*)p1 - *(int*)p2;
}

排序整型数组的完整代码:

#include <stdio.h>void Swap(char* p1, char* p2,size_t width)
{int i = 0;for (i = 0; i < width; i++){char tmp = *p1;*p1 = *p2;*p2 = tmp;p1++;p2++;}
}
void bubble_sort2(void* base, size_t sz, size_t width, int (*cmp)(const void* p1, const void* p2))
{int i = 0;//趟数for (i = 0; i < sz - 1; i++){int j = 0;//一趟冒泡排序for (j = 0; j < sz - 1 - i; j++){//比较两个元素的大小if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0){//Swap用于交换两个元素的位置Swap((char*)base + j * width, (char*)base + (j + 1) * width,width);}}}
}int cmp_int(const void* p1, const void* p2)
{return *(int*)p1 - *(int*)p2;
}int main()
{int arr[] = { 1,4,7,2,5,8,3,10,6,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;
}

代码运行结果如下:
在这里插入图片描述

7. 使用bubble_sort2来排序结构体数组

由于结构体中的数据类型较多,可以选择一种来排序,然后根据不同的数据类型来排序
按以下两种数据来举例子

struct Stu 
{char name[20];int age;
};```c
int main()
{struct Stu arr[] = { {"zhangsan",25},{"lisi",18} ,{"wangwu",30} };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort2(arr, sz, sizeof(arr[0]), cmp_struct_by_name);int i = 0;for (i = 0; i < sz; i++){printf("%s %d\n", arr[i].name, arr[i].age);}return 0;
}

7.1 按名字来排序结构体数组

字符串的比较是比较ASCII,不是比较字符串的长度

int cmp_struct_by_name(const void* p1, const void* p2)
{return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);//return strcmp((*(struct Stu*)p1).name, (*(struct Stu*)p2).name);//两段代码等价
}

完整代码如下:

#include <stdio.h>
#include <string.h>void Swap(char* p1, char* p2,size_t width)
{int i = 0;for (i = 0; i < width; i++){char tmp = *p1;*p1 = *p2;*p2 = tmp;p1++;p2++;}
}
void bubble_sort2(void* base, size_t sz, size_t width, int (*cmp)(const void* p1, const void* p2))
{int i = 0;//趟数for (i = 0; i < sz - 1; i++){int j = 0;//一趟冒泡排序for (j = 0; j < sz - 1 - i; j++){//比较两个元素的大小if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0){//Swap用于交换两个元素的位置Swap((char*)base + j * width, (char*)base + (j + 1) * width,width);}}}
}struct Stu 
{char name[20];int age;
};int cmp_struct_by_name(const void* p1, const void* p2)
{return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);//return strcmp((*(struct Stu*)p1).name, (*(struct Stu*)p2).name);//两段代码等价
}int main()
{struct Stu arr[] = { {"zhangsan",25},{"lisi",18} ,{"wangwu",30} };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort2(arr, sz, sizeof(arr[0]), cmp_struct_by_name);int i = 0;for (i = 0; i < sz; i++){printf("%s %d\n", arr[i].name, arr[i].age);}return 0;
}

代码运行结果如下:
在这里插入图片描述

7.2 按年龄来排序结构体数组

int cmp_struct_by_age(const void* p1, const void* p2)
{return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age;//return (*(struct Stu*)p1).age - (*(struct Stu*)p2).age;//两段代码等价
}

完整代码如下:

#include <stdio.h>
void Swap(char* p1, char* p2,size_t width)
{int i = 0;for (i = 0; i < width; i++){char tmp = *p1;*p1 = *p2;*p2 = tmp;p1++;p2++;}
}
void bubble_sort2(void* base, size_t sz, size_t width, int (*cmp)(const void* p1, const void* p2))
{int i = 0;//趟数for (i = 0; i < sz - 1; i++){int j = 0;//一趟冒泡排序for (j = 0; j < sz - 1 - i; j++){//比较两个元素的大小if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0){//Swap用于交换两个元素的位置Swap((char*)base + j * width, (char*)base + (j + 1) * width,width);}}}
}struct Stu 
{char name[20];int age;
};int cmp_struct_by_age(const void* p1, const void* p2)
{return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age;//return (*(struct Stu*)p1).age - (*(struct Stu*)p2).age;//两段代码等价
}
int main()
{struct Stu arr[] = { {"zhangsan",25},{"lisi",18} ,{"wangwu",30} };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort2(arr, sz, sizeof(arr[0]), cmp_struct_by_age);int i = 0;for (i = 0; i < sz; i++){printf("%s %d\n", arr[i].name, arr[i].age);}return 0;
}

代码运行结果如下:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/150911.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

golang学习笔记——接口interfaces

文章目录 Go 语言接口例子空接口空接口的定义空接口的应用空接口作为函数的参数空接口作为map的值 类型断言接口值 类型断言例子001类型断言例子002类型断言例子003巩固练习 Go 语言接口 接口&#xff08;interface&#xff09;定义了一个对象的行为规范&#xff0c;只定义规范…

基于java web个人财务管理系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

stable-diffusion-webui之webui.py

主要就是webui的启动这块&#xff0c;需要初始化的地方&#xff0c;东西还是挺多的。

8年资深测试,自动化测试常见问题总结,惊险避坑...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、自动化测试简介…

2023.11.18 Hadoop之 YARN

1.简介 Apache Hadoop YARN &#xff08;Yet Another Resource Negotiator&#xff0c;另一种资源协调者&#xff09;是一种新的 Hadoop 资源管理器&#xff0c;它是一个通用资源管理系统和调度平台&#xff0c;可为上层应用提供统一的资源管理和调度。支持多个数据处理框架&…

《轻购优品》新零售玩法:消费积分认购+众筹新玩法

《轻购优品》新零售玩法&#xff1a;消费积分认购众筹新玩法 引言&#xff1a;2023年开年已来&#xff0c;政府的工作报告提出“把恢复和扩大消费摆在优先位置”&#xff0c;并且把2023年定位为“消费提振年”&#xff0c;以“全年乐享全年盛惠”为主题多地政府共同发力&#x…

Altium Designer 相同模块的布局布线复用-AD

1、利用交互式布线&#xff0c;将两个相同模块的元器件在PCB上分块显示。 在原理图中&#xff0c;框选某一模块电路、按快捷键 TS 切换到PCB编辑界面、工具>器件摆放>在矩形区域内排列&#xff08;可将模块中的器件都集中放置到矩形框内&#xff09;。2、为模块电路添加 …

YOLOv8改进 | EIoU、SIoU、WIoU、DIoU、FocusIoU等二十余种损失函数

一、本文介绍 这篇文章介绍了YOLOv8的重大改进&#xff0c;特别是在损失函数方面的创新。它不仅包括了多种IoU损失函数的改进和变体&#xff0c;如SIoU、WIoU、GIoU、DIoU、EIOU、CIoU&#xff0c;还融合了“Focus”思想&#xff0c;创造了一系列新的损失函数。这些组合形式的…

Java方法中不使用的对象应该手动赋值为NULL吗?

在java方法中&#xff0c;不使用的对象是否应该手动赋值为null&#xff1f;我们先来通过一个示例看一下。 垃圾回收示例一 public class GuoGuoTest {public static void main(String[] args) {byte[] placeholder new byte[64 * 1024 * 1024];System.gc();} } 上面代码向内…

vue3 tsx 项目中使用 Antv/G2 实现多线折线图

Antv/G2 文档 Antv/G2 双折线图 安装 antV-G2 通过 npm 安装 项目中安装 antv/g2 依赖库&#xff1a; npm install antv/g2 --save安装成功&#xff1a; 浏览器引入 可以将脚本下载到本地&#xff0c;也可以直接引入在线资源。 引入在线资源 <!-- 引入在线资源&…

java springboot在测试类中启动一个web环境

我们在开发过程中 可以对数据层 业务层做测试 那我们的表现层能做测试吗&#xff1f; 答案自然是可以的 但是 前提 我们要有一个web环境 我们现在 测试类运行 明显是个很普通的java程序 还是这个 SpringBootTest 它有一个 webEnvironment 我们可以先这样写 package com.examp…

城市生命线丨市政综合管廊监测系统的效果

市政综合管廊&#xff0c;又被称为城市生命线&#xff0c;是我们在地下建造的一个智慧而高效的空间。它把市政、电力、通讯、燃气、给排水等各种管线集于一体&#xff0c;解决了城市中反复开挖路面、架空线网密集、管线事故频发等问题&#xff0c;为城市运行提供了重要的基础设…

Windows安装Vmware 虚拟机

目录 一、Vmware 虚拟机介绍 二、Vmware 虚拟机的三种网络模式 2.1桥接模式 2.2仅主机模式 2.3NAT 网络地址转换模式 三、Vmware 虚拟机的安装 一、Vmware 虚拟机介绍 VMware Workstation Pro 是一款可以在个人电脑的操作系统上创建一个完全与主机操作系统隔离的 虚拟机&…

【技巧】Word和Excel如何互相转换?

Word文档里有数据表格&#xff0c;如果编辑修改起来感觉没那么方便或容易出错&#xff0c;不妨将文档转换成Excel表格再来处理。 将Word文档转换成Excel&#xff0c;比较常用的是复制粘贴方法&#xff0c;也就是将Word文档的表格复制后&#xff0c;再粘贴到Excel表格里&#x…

FPC焊点剥离失效分析

一、案例背景 FPC在后续组装过程中&#xff0c;连接器发生脱落。在对同批次的样品进行推力测试后&#xff0c;发现连接器推力有偏小的现象。据此进行失效分析&#xff0c;明确FPC连接器脱落原因。 #1、#2样品连接器脱落连接器脱落&#xff1b;#3样品连接器未脱落&#xff1b;…

HTTP HTTPS 独特的魅力

目录 HTTP协议 HTTP协议的工作过程 首行 请求头&#xff08;header&#xff09; HOST Content-Length​编辑 User-Agent&#xff08;简称UA&#xff09; Referer Cookie 空行 正文&#xff08;body&#xff09; HTTP响应详解 状态码 报文格式 HTTP响应格式 如何…

鸿蒙:Harmony开发基础知识详解

一.概述 工欲善其事&#xff0c;必先利其器。 上一篇博文实现了一个"Hello Harmony"的Demo&#xff0c;今天这篇博文就以"Hello Harmony" 为例&#xff0c;以官网开发文档为依据&#xff0c;从鸿蒙开发主要的几个方面入手&#xff0c;详细了解一下鸿蒙开…

RAID技术复习笔记

Raid&#xff08;Redundant Array of independent Disks&#xff09;独立磁盘冗余阵列&#xff1a;磁盘阵列 Raid 分为:软raid、硬raid、软硬混合三种。 软Raid&#xff1a;所有的功能均有操作系统和CPU来完成&#xff0c;没有独立的raid控制、处理芯片和IO处理处理芯片。 硬R…

车载通信架构 —— 传统车内通信网络发展回顾

车载通信架构 —— 传统车内通信网络发展回顾 我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 屏蔽力是信息过载时代一个人的特殊竞争力&#xff0c;任何…

c语言-数据结构-堆

目录 一、二叉树 1、二叉树的概念 2、完全二叉树和满二叉树 3、完全二叉树的顺序存储 二、堆 2、堆的概念与结构 3、堆的创建及初始化 4、堆的插入&#xff08;小堆&#xff09; 5、堆的删除 6、显示堆顶元素 7、显示堆里的元素个数 8、测试堆的各个功能 9、 实现堆…