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.rhkb.cn/news/198199.html

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

相关文章

2023.11.19 hadoop之MapReduce

目录 1.简介 2.分布式计算框架-Map Reduce 3.mapreduce的步骤 4.MapReduce底层原理 map阶段 shuffle阶段 reduce阶段 1.简介 Mapreduce是一个分布式运算程序的编程框架&#xff0c;是用户开发“基于hadoop的数据分析应用”的核心框架&#xff1b; Mapreduce核心功能是…

辅助笔记-Jupyter Notebook的安装和使用

辅助笔记-Jupyter Notebook的安装和使用 文章目录 辅助笔记-Jupyter Notebook的安装和使用1. 安装Anaconda2. conda更换清华源3. Jupter Notebooks 使用技巧 笔记主要参考B站视频“最易上手的Python环境配置——Jupyter Notebook使用精讲”。 Jupyter Notebook (此前被称为IPyt…

配置iTerm2打开自动执行命令

打开iTerm2&#xff0c;commado&#xff0c;打开profies->edit profies&#xff0c;点击号&#xff0c;创建一个新的profile 在新的profile中填写 name&#xff1a;随意 command&#xff1a;Login Shell Send text at start&#xff1a;执行脚本的命令&#xff0c;不想写路…

nodejs+vue实验室上机管理系统的设计与实现-微信小程序-安卓-python-PHP-计算机毕业设计

用户&#xff1a;管理员、教师、学生 基础功能&#xff1a;管理课表、管理机房情况、预约机房预约&#xff1b;权限不同&#xff0c;预约类型不同&#xff0c;教师可选课堂预约和个人&#xff1b;课堂预约。 在实验室上机前&#xff0c;实验室管理员需要对教务处发来的上机课表…

2023/11/19总结

项目进度&#xff1a; 地址管理&#xff1a; 显示菜品 购物车相关功能 然后最近在看 支付宝沙盒支付的相关功能&#xff0c;打算把支付给做了 。界面做的不是很好看 &#xff0c;但是后续会改成 手机端的。

wpf devexpress 创建布局

模板解决方案 例子是一个演示连接数据库连接程序。打开RegistrationForm.BaseProject项目和如下步骤 RegistrationForm.Lesson1 项目包含结果 审查Form设计 使用LayoutControl套件创建混合控件和布局 LayoutControl套件包含三个主控件&#xff1a; LayoutControl - 根布局…

Taro.navigateTo 使用URL传参数和目标页面参数获取

文章目录 1. Taro.navigateTo 简介2. 通过 URL 传递参数3. 目标页面参数获取4. 拓展与分析4.1 拓展4.2 URL参数的类型4.3 页面间通信 5. 总结 &#x1f389;欢迎来到Java学习路线专栏~Taro.navigateTo 使用URL传参数和目标页面参数获取 ☆* o(≧▽≦)o *☆嗨~我是IT陈寒&#x…

基于springboot实现摄影跟拍预定管理系统【项目源码+论文说明】

基于springboot实现摄影跟拍预定管理系统演示 摘要 首先,论文一开始便是清楚的论述了系统的研究内容。其次,剖析系统需求分析,弄明白“做什么”,分析包括业务分析和业务流程的分析以及用例分析,更进一步明确系统的需求。然后在明白了系统的需求基础上需要进一步地设计系统,主要…

动态规划专项---最长上升子序列模型

文章目录 怪盗基德的滑翔翼登山合唱队形友好城市最大上升子序列和拦截导弹导弹防御系统最长公共上升子序列 一、怪盗基德的滑翔翼OJ链接 本题思路:本题是上升子序列模型中比较简单的模型&#xff0c;分别是从前往后和从后往前走一遍LIS即可。 #include <bits/stdc.h>co…

【c++随笔13】多态

【c随笔13】多态 多态性&#xff08;Polymorphism&#xff09;在面向对象编程中是一个重要概念&#xff0c;它允许以统一的方式处理不同类型的对象&#xff0c;并在运行时动态确定实际执行的方法或函数。一、什么是多态性&#xff1f;1、关键概念&#xff1a;C的多态性2、多态定…

算法——动态规划(新)

什么是动态规划&#xff1f; 动态规划算法的基本思想-求解步骤-基本要素和一些经典的动态规划问题【干货】-CSDN博客 一、三步问题 面试题 08.01. 三步问题 - 力扣&#xff08;LeetCode&#xff09; 思路 我们要知道&#xff0c;走楼梯&#xff0c;前三个阶梯步数已经知道&…

6 Redis的慢查询配置原理

1、redis的命令执行流程 redis的慢查询只针对步骤3 默认情况下&#xff0c;慢查询的阈值是10ms

Ps:变换

可以向选区、整个图层、多个图层或图层蒙版应用变换 Transform&#xff0c;还可以向路径、矢量形状、矢量蒙版、选区边界或 Alpha 通道应用变换。 若要变换栅格&#xff08;像素&#xff09;图像&#xff0c;建议先将其转换为智能对象&#xff0c;以便进行非破坏性的变换。 Ps菜…

【Django-DRF用法】多年积累md笔记,第(4)篇:Django-DRF反序列化详解

本文从分析现在流行的前后端分离Web应用模式说起&#xff0c;然后介绍如何设计REST API&#xff0c;通过使用Django来实现一个REST API为例&#xff0c;明确后端开发REST API要做的最核心工作&#xff0c;然后介绍Django REST framework能帮助我们简化开发REST API的工作。 全…

[内存泄漏][PyTorch](create_graph=True)

PyTorch保存计算图导致内存泄漏 1. 内存泄漏定义2. 问题发现背景3. github中pytorch源码关于这个问题的讨论 1. 内存泄漏定义 内存泄漏&#xff08;Memory Leak&#xff09;是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放&#xff0c;造成系统内存的浪费&#…

MIB 6.1810实验Xv6 and Unix utilities(3)pingpong

Mit6.S081-实验1-Xv6 and Unix utilities-pingpong问题_Isana_Yashiro的博客-CSDN博客 Write a user-level program that uses xv6 system calls to ping-pong a byte between two processes over a pair of pipes, one for each direction. The parent should send a byte to…

【计算思维】蓝桥杯STEMA 科技素养考试真题及解析 4

1、下列哪个选项填到填到下图空缺处最合适 A、 B、 C、 D、 答案&#xff1a;D 2、按照如下图的规律摆放正方形&#xff0c;第 5 堆正方形的个数是 A、13 B、14 C、15 D、16 答案&#xff1a;D 3、从右面观察下面的立体图形&#xff0c;看到的是 A、 B、 C、 D、 答…

hyperledger fabric2.4测试网络添加组织数量

!!!修改内容比较繁琐,预期未来提供模板修改 修改初始配置文件,初始添加3个组织 organizations文件夹 /cryptogen文件夹下创建文件crypto-config-org3.yaml,内容如下: PeerOrgs:# ---------------------------------------------------------------------------# Org3# ----…

STM32电源名词解析

先来简单了解一下各种电源端口的命名 VCC&#xff1a;Ccircuit 表示电路的意思, 即接入电路的电压 VDD&#xff1a;Ddevice 表示器件的意思, 即器件内部的工作电压。 VSS&#xff1a;Sseries 表示公共连接的意思&#xff0c;通常指电路公共接地端电压。 GND&#xff1a;在电…

SpringCloud微服务注册中心:Nacos介绍,微服务注册,Ribbon通信,Ribbon负载均衡,Nacos配置管理详细介绍

微服务注册中心 注册中心可以说是微服务架构中的”通讯录“&#xff0c;它记录了服务和服务地址的映射关系。在分布式架构中&#xff0c;服务会注册到这里&#xff0c;当服务需要调用其它服务时&#xff0c;就这里找到服务的地址&#xff0c;进行调用。 微服务注册中心 服务注…