当前位置: 首页 > news >正文

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;
}

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

相关文章:

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.…...

数字化未来:实时云渲染在智慧城市中的创新应用

数字中国战略"是国家推动数字经济发展的战略框架。这个战略旨在加速数字化转型&#xff0c;推动信息技术在各个领域的应用&#xff0c;提高社会经济效益和人民生活质量。而智慧城市作为其中的重要一环&#xff0c;重要性不言而喻。 智慧城市是当今城市发展的热点和趋势&a…...

Go语言常用命令详解(二)

文章目录 前言常用命令go bug示例参数说明 go doc示例参数说明 go env示例 go fix示例 go fmt示例 go generate示例 总结写在最后 前言 接着上一篇继续介绍Go语言的常用命令 常用命令 以下是一些常用的Go命令&#xff0c;这些命令可以帮助您在Go开发中进行编译、测试、运行和…...

ChatGPT 从零到一打造私人智能英语学习助手

近几年&#xff0c;随着智能化技术的发展和人工智能的兴起&#xff0c;越来越多的应用程序开始涌现出来。在这些应用中&#xff0c;语音识别、自然语言处理以及机器翻译等技术都得到了广泛的应用。其中&#xff0c;聊天机器人成为了最受欢迎的人工智能应用之一&#xff0c;它们…...

算法升级之路(七)-盛最多水的容器

给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线&#xff0c;使得它们与 x 轴共同构成的容器可以容纳最多的水。 返回容器可以储存的最大水量。 原题链接: 盛最多水的容器 解题思路&…...

milvus数据库索引管理

一、建立向量索引 默认情况下&#xff0c;Milvus不会对小于1,024行的段进行索引。 1.准备索引参数 index_params {"metric_type":"L2","index_type":"IVF_FLAT","params":{"nlist":1024} } #"nlist"…...

JVM中的 -Xms参数 设置 JVM 的初始堆大小

在 Java 虚拟机&#xff08;JVM&#xff09;的配置中&#xff0c;-Xms 是一个启动参数&#xff0c;用于设置 JVM 的初始堆大小&#xff08;Initial Heap Size&#xff09;。这个参数对于优化 Java 应用程序的性能非常重要&#xff0c;特别是在处理需要大量内存的应用程序时。 …...

Idea 创建 Spring 项目(保姆级)

描述信息 最近卷起来&#xff0c;系统学习Spring&#xff1b;俗话说&#xff1a;万事开头难&#xff1b;创建一个Spring项目在网上找了好久没有找到好的方式&#xff1b;摸索了半天产出如下文档。 在 Idea 中新建项目 填写信息如下 生成项目目录结构 pom添加依赖 <depende…...

C++多线程学习(一):C++11 多线程快速入门

参考引用 C11 14 17 20 多线程从原理到线程池实战代码运行环境&#xff1a;Visual Studio 2019 1. 为什么要用多线程 任务分解 耗时的操作&#xff0c;任务分解&#xff0c;实时响应 数据分解 充分利用多核CPU处理数据 数据流分解 读写分离&#xff0c;解耦合设计 2. 第一个…...

Linux系统之lsof命令的基本使用

Linux系统之lsof命令的基本使用 一、lsof命令的基本使用二、lsof命令的使用帮助2.1 lsof命令的help帮助信息2.2 lsof命令帮助解释 三、lsof的基本使用3.1 直接使用lsof命令3.2 查看某个进程打开的所有文件3.3 查看某个用户打开的所有文件3.4 查看某个文件被哪些进程打开3.5 查看…...

性能压力测试的优势与重要性

性能压力测试是软件开发过程中至关重要的一环&#xff0c;它通过模拟系统在极限条件下的运行&#xff0c;以评估系统在正常和异常负载下的表现。这种测试为确保软件系统的可靠性、稳定性和可伸缩性提供了关键信息。下面将探讨性能压力测试的优势以及为什么在软件开发中它具有不…...

AtCoder Beginner Contest 329 题解A~F

A - Spread 输入字符串&#xff0c;字符之间加上空格输出 B - Next 输出数组当中第二大的数 C - Count xxx 统计每个字符出现过的最长长度&#xff0c;再累加即可 #include<bits/stdc.h> #pragma GCC optimize("Ofast") #define INF 0x3f3f3f3f #define I…...

Windows网络「SSL错误问题」及解决方案

文章目录 问题方案 问题 当我们使用了神秘力量加持网络后&#xff0c;可能会和国内的镜像源网站的之间发生冲突&#xff0c;典型的有 Python 从网络中安装包&#xff0c;如执行 pip install pingouin 时&#xff0c;受网络影响导致无法完成安装的情况&#xff1a; pip config…...

python数据可视化

绘制简单的折线图 1.1json数据格式 JSON是一种轻量级的数据交互格式。可以按照JSON指定的格式去组织和封装数据&#xff0c;其本质上是一个带有特定格式的字符串。 主要功能&#xff1a;json就是一种在各个编程语言中流通的数据格式&#xff0c;负责不同编程语言中的数据传递…...

LV.12 D18 中断处理 学习笔记

一、ARM的异常处理机制及工程代码结构 1.1异常概念 处理器在正常执行程序的过程中可能会遇到一些不正常的事件发生 这时处理器就要将当前的程序暂停下来转而去处理这个异常的事件 异常事件处理完成之后再返回到被异常打断的点继续执行程序。 1.2异常处理机制 不同的处…...

蓝桥杯每日一题2023.11.19

题目描述 “蓝桥杯”练习系统 (lanqiao.cn) 题目分析 首先想到的方法为dfs去寻找每一个数&#xff0c;但发现会有超时 #include<bits/stdc.h> using namespace std; const int N 2e5 10; int n, cnt, a[N]; void dfs(int dep, int sum, int start) {if(dep 4){if(s…...

<b><strong>,<i><em>标签的区别

1. b标签和strong标签 b标签&#xff1a;仅仅是UI层面的加粗样式&#xff0c;并不具备HTML语义 strong标签&#xff1a;不仅是在UI层面的加粗样式&#xff0c;具备HTML语义&#xff0c;表示强调 2. i标签和em标签 i 标签&#xff1a;仅仅是UI层面的斜体样式&#xff0c;并不具备…...

c++中的特殊类设计

文章目录 1.请设计一个类&#xff0c;不能被拷贝2. 请设计一个类&#xff0c;只能在堆上创建对象3. 请设计一个类&#xff0c;只能在栈上创建对象4. 请设计一个类&#xff0c;不能被继承5. 请设计一个类&#xff0c;只能创建一个对象(单例模式) 1.请设计一个类&#xff0c;不能…...

开源更安全? yum源配置/rpm 什么是SSH?

文章目录 1.开放源码有利于系统安全2.yum源配置&#xff0c;这一篇就够了&#xff01;(包括本地&#xff0c;网络&#xff0c;本地共享yum源)3.rpm包是什么4.SSH是什么意思&#xff1f;有什么功能&#xff1f; 1.开放源码有利于系统安全 开放源码有利于系统安全 2.yum源配置…...

庖丁解牛:NIO核心概念与机制详解 04 _ 分散和聚集

文章目录 Pre概述分散/聚集 I/O分散/聚集的应用聚集写入Code Pre 庖丁解牛&#xff1a;NIO核心概念与机制详解 01 庖丁解牛&#xff1a;NIO核心概念与机制详解 02 _ 缓冲区的细节实现 庖丁解牛&#xff1a;NIO核心概念与机制详解 03 _ 缓冲区分配、包装和分片 概述 分散/聚…...

基于算法竞赛的c++编程(28)结构体的进阶应用

结构体的嵌套与复杂数据组织 在C中&#xff0c;结构体可以嵌套使用&#xff0c;形成更复杂的数据结构。例如&#xff0c;可以通过嵌套结构体描述多层级数据关系&#xff1a; struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…...

7.4.分块查找

一.分块查找的算法思想&#xff1a; 1.实例&#xff1a; 以上述图片的顺序表为例&#xff0c; 该顺序表的数据元素从整体来看是乱序的&#xff0c;但如果把这些数据元素分成一块一块的小区间&#xff0c; 第一个区间[0,1]索引上的数据元素都是小于等于10的&#xff0c; 第二…...

SkyWalking 10.2.0 SWCK 配置过程

SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外&#xff0c;K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案&#xff0c;全安装在K8S群集中。 具体可参…...

基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销&#xff0c;平衡网络负载&#xff0c;延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...

STM32+rt-thread判断是否联网

一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...

抖音增长新引擎:品融电商,一站式全案代运营领跑者

抖音增长新引擎&#xff1a;品融电商&#xff0c;一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中&#xff0c;品牌如何破浪前行&#xff1f;自建团队成本高、效果难控&#xff1b;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...

Frozen-Flask :将 Flask 应用“冻结”为静态文件

Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是&#xff1a;将一个 Flask Web 应用生成成纯静态 HTML 文件&#xff0c;从而可以部署到静态网站托管服务上&#xff0c;如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...

[Java恶补day16] 238.除自身以外数组的乘积

给你一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法&#xff0c;且在 O(n) 时间复杂度…...

Map相关知识

数据结构 二叉树 二叉树&#xff0c;顾名思义&#xff0c;每个节点最多有两个“叉”&#xff0c;也就是两个子节点&#xff0c;分别是左子 节点和右子节点。不过&#xff0c;二叉树并不要求每个节点都有两个子节点&#xff0c;有的节点只 有左子节点&#xff0c;有的节点只有…...