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

qsort函数排序数据 and 模拟实现qosrt函数(详解)

前言:内容包括使用库函数qsort排序任意类型的数据,模拟实现qsort函数(冒泡排序的逻辑)

我们先了解qsort函数的语法:qsort函数默认按照升序排序数据

void qsort (void* base, size_t num, size_t size,int (*compar)(const void*,const void*));

qsort函数的头文件:

#include<stdlib.h>

qsort函数有4个参数:

第一个参数 void*base:指向待排序数组的第一个元素的指针

第二个参数 size_t num:待排序数组的元素个数 

第三个参数 size_t size: 待排序数组的每个元素的所占空间的大小,单位是字节

第四个参数  int (*compar)(const void*,const void*):指向一个能够比较两个元素大小的函数,即一个函数指针,此函数需要使用者自己设计

由于设计qsort函数的人不知道 未来会是谁使用qsort函数排序哪种类型的数据,故而设计void*指针,从而能够接收任意类型数据的地址

比较两个元素大小的函数的设计规则:

 int (*compar)(const void*,const void*))

此函数的返回值:

返回值意义
<0第一个元素<第二个元素
0第一个元素=第二个元素
>0第一个元素>第二个元素

 part 1:qsort函数排序整型数据

#include<stdio.h>
#include<stdlib.h>
void Print(int arr[], int sz)
{int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}
}int cmp_int(const void* p1, const void* p2)
{return *(int*)p1 - *(int*)p2; //升序//将指针p1和p2强制类型转换成int*类型,然后对其解引用//  *(int*)p2 - *(int*)p1 降序}int main()
{int arr[10] = { 5,7,3,9,1,4,6,2,8,10 };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_int);Print(arr, sz);return 0;
}

由于qsort函数默认是升序排列的,若是想要降序排列,只需将p1的位置写成p2,p2的位置写成p1 

void*的指针不能直接解引用,和++,--操作,

需要将void*类型转成你需要的类型 

part 2:qsort函数排序浮点型数据

#include<stdio.h>
#include<stdlib.h>
void Print(float arr[], int sz)
{int i = 0;for (i = 0; i < sz; i++){printf("%f\n", arr[i]);}
}
int cmp_flo(const void* p1, const void* p2)
{if (*(float*)p1 - *(float*)p2 > 0.000000)//升序{return 1; // 第一个元素大于第二个元素// 降序:*(float*)p2 - *(float*)p1 > 0.000000}else if (*(float*)p1 - *(float*)p2 < 0.000000){return -1; //第一个元素小于第二个元素// 降序:*(float*)p2 - *(float*)p1 < 0.000000}else{return 0; //第一个元素等于第二个元素}
}
int main()
{float arr[] = { 1.2f,1.1f,1.8f };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_flo);Print(arr, sz);return 0;
}

降序排列需要将所有的p1写成p2,p2写成p1

part 3:qsort函数排序字符串

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int cmp_str(const void* p1, const void* p2)
{return strcmp((char*)p1, (char*)p2);//升序//降序:strcmp((char*)p2, (char*)p1)
}
int main()
{char arr[] = "ceadb";int len = strlen(arr);qsort(arr, len, sizeof(arr[0]), cmp_str);puts(arr); //结果是abcdereturn 0;
}

part 4:qsort函数排序字符串数组 

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void Print(char* arr[], int sz)
{int i = 0;for (i = 0; i < sz; i++){printf("%s\n", arr[i]);}
}
int cmp_by_len(const void* p1, const void* p2)
{return strlen(*(char**)p1) - strlen(*(char**)p2);//升序//降序:strlen(*(char**)p2) - strlen(*(char**)p1)
}
int main()
{char* arr[] = { "san","yi","ling" };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_by_len);Print(arr, sz);return 0;
}

排序按照字符串的长度排序 

一级指针数据的地址需要二级指针来接收,故而需要将指针p1和p2强制转换成二级指针(char**)

part 5 qsort函数排序结构体数据 

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct Stu
{char name[10];int age;
};
/*int cmp_by_age(const void* p1, const void* p2)
{return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age;
}
*/
int cmp_by_name(const void* p1, const void* p2)
{return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);
}
int main()
{struct Stu s[] = { {"zhangsan",30},{"lisi",15},{"wangwa",20} };int sz = sizeof(s) / sizeof(s[0]);/*qsort(s, sz, sizeof(s[0]), cmp_by_age);*/qsort(s, sz, sizeof(s[0]), cmp_by_name);return 0;
}

结构体数据类型的排序需要首先确定按照什么来排序,这里设定一个学生类型包括:名字,年龄

故而我们有两种排序规则:其一:按照名字来排序,其二:按照年龄来排序 

 part 6:运用冒泡排序思维模拟实现qsort函数

void Swap(char* buf1, char* buf2,int width)
{int i = 0;for (i = 0; i < width; i++){char tmp = *buf1;*buf1 = *buf2;*buf2 = tmp;buf1++;buf2++;}
}void bubble_sort(void* base, size_t sz, size_t width, int(*cmp)(const void*, const void*))
{size_t i = 0;for (i = 0; i < sz - 1; i++){size_t j = 0;int flag = 1;for (j = 0; j < sz - 1 - i; j++){if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0){Swap((char*)base + j * width, (char*)base + (j + 1) * width,width);flag = 0;}}if (flag == 1){break;}}
}

我设定模拟qsort函数功能的函数名为bubble_sort,按照升序排列数据

冒泡排序我就不详解了

base指针:指向待排序数组的第一个元素

sz:无符号数(即正数),代表待排序数据的个数

width:一个数据所占内存空间的大小,单位是字节

cmp指针:指向一个由使用者设计的能够比较两个元素大小的函数,即cmp是一个函数指针

flag:标记某一趟排序的数组是否已经有序

1 sz个数据,共需进行sz-1趟冒泡排序

2 一趟冒泡排序内部:

首先都假设此趟数据已经有序,flag=1,若是发生了交换,则将flag置为0,一趟冒泡排序结束后判断flag的值,若是flag仍为1,说明在这一趟冒泡排序中没有数据发生交换,则表明此组数据已经有序,结束循环,若是flag变成0,说明这一趟冒泡排序由数据发生交换,表明此组数据还未有序,继续下一趟冒泡排序

 a. 若是j为下标的元素>j+1为下标的元素,则需要交换

    交换:1 若是cmp函数(比较两个元素的大小)返回值>0:第一个元素大于第二个元素,交换

        2 使用char*指针,将两个元素一个字节一个字节地完成交换,这样能够交换任意类型的数据

将指向首元素的指针base强制转换成char*类型,这样一次能够跳过一个字节

可能会有疑惑为什么不降base指针转成其他类型的指针,比如int*

因为只有char*能够一次跳过一个字节,这样对于任意类型的数据都能进行操作

 要是转成int*类型的指针,一次能够跳过4个字节,不能够随意对非整型的数据进行操作

 

(char*)base + j * width, (char*)base + (j + 1) * width
base指针跳过j个大小为width字节的元素
base指针跳过(j+1)个大小为width字节的元素

可以联想普通的冒泡排序交换数组中两个数据的方法:交换arr[j], arr[j+1]

Swap函数完成两个元素每一个字节的交换

void Swap(char* buf1, char* buf2,int width)
{int i = 0;for (i = 0; i < width; i++){char tmp = *buf1;*buf1 = *buf2;*buf2 = tmp;buf1++;buf2++;}
}

比如数字3和数字4进行交换:

若是小端存储:

03 00 00 00 04 00 00 00   

buf1指向03 buf2指向04,交换完一对字节后,分别++,指向下一对要交换的字节

对应的颜色进行交换 03和04交换……

下面是使用模拟实现qsort函数功能的函数排序整型数组:

int cmp_int(const void* p1, const void* p2)
{return *(int*)p1 - *(int*)p2;//这是升序//降序:*(int*)p2- *(int*)p1
}
// cmp_int 函数需要我们自己设计
void Print(int arr[], int sz)
{int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}
}void Swap(char* buf1, char* buf2,int width)
{int i = 0;for (i = 0; i < width; i++){char tmp = *buf1;*buf1 = *buf2;*buf2 = tmp;buf1++;buf2++;}
}
void bubble_sort(void* base, size_t sz, size_t width, int(*cmp)(const void*, const void*))
{size_t i = 0;for (i = 0; i < sz - 1; i++){size_t j = 0;int flag = 1;for (j = 0; j < sz - 1 - i; j++){if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0){Swap((char*)base + j * width, (char*)base + (j + 1) * width,width);flag = 0;}}if (flag == 1){break;}}
}
int main()
{int arr[10] = { 3,6,1,7,2,8,10,9,4,5 };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);Print(arr, sz);return 0;
}

当然你也可以使用自己模拟实现的qsort函数去排序其他类型的数据,比较不同类型元素大小的函数在上方都有

相关文章:

qsort函数排序数据 and 模拟实现qosrt函数(详解)

前言&#xff1a;内容包括使用库函数qsort排序任意类型的数据&#xff0c;模拟实现qsort函数&#xff08;冒泡排序的逻辑&#xff09; 我们先了解qsort函数的语法&#xff1a;qsort函数默认按照升序排序数据 void qsort (void* base, size_t num, size_t size,int (*compar)(…...

Mysql视图,存储过程,触发器,函数以及Mysql架构

一,视图视图是基于查询的一个虚拟表 , 也就是将sql语句封装起来, 要用的时候直接调用视图即可, select语句查询的表称为基表, 查询的结果集称为虚拟表, 基本表数据发生了改变, 那么视图也会发生改变, 使用视图就是为了简化查询语句.1.CREATE VIEW view_admin AS SELECT * FROM…...

什么是线程死锁?如何解决死锁问题

死锁&#xff0c;一组互相竞争的资源的线程之间相互等待&#xff0c;导致永久阻塞的现象。 如下图所示&#xff1a; 与死锁对应的&#xff0c;还有活锁&#xff0c;是指线程没有出现阻塞&#xff0c;但是无限循环。 有一个经典的银行转账例子如下&#xff1a; 我们有个账户类…...

C语言几种判断语句简述

C 判断 判断结构要求程序员指定一个或多个要评估或测试的条件&#xff0c;以及条件为真时要执行的语句&#xff08;必需的&#xff09;和条件为假时要执行的语句&#xff08;可选的&#xff09;。 C 语言把任何非零和非空的值假定为 true&#xff0c;把零或 null 假定为 fals…...

【python学习笔记】:SQL常用脚本(二)

11、四舍五入ROUND函数 ROUND ( numeric_expression , length [ ,function ] ) function 必须为 tinyint、smallint 或 int。 如果省略 function 或其值为 0&#xff08;默认值&#xff09;&#xff0c;则将舍入 numeric_expression。 如果指定了0以外的值&#xff0c;则将截…...

【Linux】进程地址空间

文章目录&#x1f3aa; 进程地址空间&#x1f680;1.写时拷贝与虚拟地址&#x1f680;2.地址空间引入&#x1f680;3.地址空间的意义⭐3.1 虚拟地址寻址⭐3.2 虚拟地址意义&#x1f3aa; 进程地址空间 地址空间&#xff08;address space&#xff09;表示任何一个计算机实体所…...

Qt音视频开发17-vlc内核回调拿图片进行绘制

一、前言 在众多播放器中&#xff0c;支持的种类格式众多&#xff0c;并支持DVD影音光盘&#xff0c;VCD影音光盘及各类流式协议&#xff0c;提供了sdk进行开发&#xff0c;这点是至关重要的&#xff0c;尽管很多优秀的播放器很牛逼&#xff0c;由于没有提供sdk第三方开发&…...

安装配置DHCP

本次实验采用CentOS71.检查在安装DHCP之前先使用rpm命令查看系统中已有的DHCP软件包rpm -qa | grep dhcp由此可知&#xff0c;系统中尚未安装DHCP软件包2.安装我们可以使用yum命令为系统安装DHCP软件包yum -y install dhcp安装完成后再次检查可以看到DHCP软件包3.配置dhcp配置文…...

MarkDown中写UML图的方法

目录序UML图之顺序图顺序图的四个要素关于消息箭头的语法Mermaid中顺序图的简单例子样例用小人表示对象为对象设置别名激活对象UML图之类图类图中常见的关系关于不同类型关系的语法Mermaid中类图的简单例子样例类定义的两种方式为类定义成员双向关系的表示多重性关系的表示UML之…...

Axure8设计—动态仪表盘

本次分享的的案例是Axure8制作的动态仪表盘,根据设置的数值&#xff0c;仪表盘指针旋转到相应的值位置 预览地址&#xff1a;https://2qiuwg.axshare.com 下载地址&#xff1a;https://download.csdn.net/download/weixin_43516258/87502161 一、制作原型 1、首先创建空白页…...

【C++】类和对象的六个默认成员函数

类的6个默认成员函数构造函数概念特性析构函数概念特性拷贝构造函数概念特征拷贝构造函数典型调用场景&#xff1a;赋值运算符重载运算符重载赋值运算符重载取地址及const取地址操作符重载类的6个默认成员函数 到底什么是类的6个默认成员函数呢&#xff1f;相信大家一定对此怀…...

4、算法MATLAB---认识矩阵

认识矩阵1、矩阵定义和基本运算1.1 赋值运算符&#xff1a;1.2 等号运算符&#xff1a;1.3 空矩阵1.4 一行一列矩阵1.5 行矩阵&#xff08;元素用空格或逗号分隔&#xff09;1.6 列矩阵&#xff08;分号表示换行&#xff09;1.7 m行n列的矩阵&#xff1a;行值用逗号间隔&#x…...

vue3+rust个人博客建站日记2-确定需求

反思 有人说过我们正在临近代码的终结点。很快&#xff0c;代码就会自动产生出来&#xff0c;不需要再人工编写。程序员完全没用了&#xff0c;因为商务人士可以从规约直接生成程序。 扯淡&#xff01;我们永远抛不掉代码&#xff0c;因为代码呈现了需求的细节。在某些层面上&a…...

Linux安装云原生网关Kong/KongA

目录1 概述2 创建服务器3 安装postgres4 安装kong5 安装node6 安装KONGA1 概述 Kong Kong是一款基于OpenResty&#xff08;NginxLua模块&#xff09;编写的高可用、易扩展的开源API网关&#xff0c;专为云原生和云混合架构而建&#xff0c;并针对微服务和分布式架构进行了特别…...

Vue学习笔记(2)

2.1 事件处理 2.1.1 事件监听器 JavaScript&#xff1a;通过获取DOM对象再往DOM对象上使用addEventListener注册监听事件 const btn document.querySelector(#my-button) btn.addEventListener(click, function() {alert(点击事件!) })jQuery&#xff1a;通过$选择器绑定对象…...

2023年三月份图形化四级打卡试题

活动时间 从2023年3月1日至3月21日&#xff0c;每天一道编程题。 本次打卡的规则如下&#xff1a; 小朋友每天利用10~15分钟做一道编程题&#xff0c;遇到问题就来群内讨论&#xff0c;我来给大家答疑。 小朋友做完题目后&#xff0c;截图到朋友圈打卡并把打卡的截图发到活动群…...

Python操作Excel

Python中对Excel文件的操作包括&#xff1a;读、写、修改。如果要对其进行如上的操作需要导入Python的第三方模块&#xff1a;xlrd、xlwd、xlutils&#xff0c;其分别对应Python的读、写、修改的操作 一、安装Python的第三方模块 二、操作Excel的基本步骤 1、导入响对应的模…...

Codeforces Round #853 (Div. 2) C. Serval and Toxel‘s Arrays【统计次数,算贡献】

链接 传送门 分析 这道题想法其实很简单&#xff0c;样例的计算方法一定要看懂。以样例1为例&#xff0c;根据他的操作方法可以得到两个新的数组&#xff0c;和一个原来的数组&#xff0c;总共三个数组。 1 2 3 4 2 3 4 5 3 他们两两配对去重&#xff0c;求出总的value。由于每…...

微信小程序-1:比较两数的大小

程序来源》微信小程序开发教程&#xff08;第二章&#xff09; 主编&#xff1a;黄寿孟、易芳、陶延涛 ISBN&#xff1a; 9787566720788 程序运行结果&#xff1a; <!--index.wxml--> <view class"container"> <text>第一个数字&#xff1a;&…...

数据结构——树

深度优先/广度优先遍历深度优先&#xff1a;访问根节点对根节点的 children 挨个进行深度优先遍历const tree {val: "a",children: [{val: "b",children: [{val: "d",children: [],},{val: "e",children: [],},],},{val: "c&quo…...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战

前言 现在我们有个如下的需求&#xff0c;设计一个邮件发奖的小系统&#xff0c; 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式&#xff08;Decorator Pattern&#xff09;允许向一个现有的对象添加新的功能&#xff0c;同时又不改变其…...

多模态2025:技术路线“神仙打架”,视频生成冲上云霄

文&#xff5c;魏琳华 编&#xff5c;王一粟 一场大会&#xff0c;聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中&#xff0c;汇集了学界、创业公司和大厂等三方的热门选手&#xff0c;关于多模态的集中讨论达到了前所未有的热度。其中&#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群集中。 具体可参…...

现代密码学 | 椭圆曲线密码学—附py代码

Elliptic Curve Cryptography 椭圆曲线密码学&#xff08;ECC&#xff09;是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础&#xff0c;例如椭圆曲线数字签…...

Redis数据倾斜问题解决

Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中&#xff0c;部分节点存储的数据量或访问量远高于其他节点&#xff0c;导致这些节点负载过高&#xff0c;影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…...

React---day11

14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store&#xff1a; 我们在使用异步的时候理应是要使用中间件的&#xff0c;但是configureStore 已经自动集成了 redux-thunk&#xff0c;注意action里面要返回函数 import { configureS…...

AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别

【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而&#xff0c;传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案&#xff0c;能够实现大范围覆盖并远程采集数据。尽管具备这些优势&#xf…...

Rust 开发环境搭建

环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行&#xff1a; rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu ​ 2、Hello World fn main() { println…...

文件上传漏洞防御全攻略

要全面防范文件上传漏洞&#xff0c;需构建多层防御体系&#xff0c;结合技术验证、存储隔离与权限控制&#xff1a; &#x1f512; 一、基础防护层 前端校验&#xff08;仅辅助&#xff09; 通过JavaScript限制文件后缀名&#xff08;白名单&#xff09;和大小&#xff0c;提…...

路由基础-路由表

本篇将会向读者介绍路由的基本概念。 前言 在一个典型的数据通信网络中&#xff0c;往往存在多个不同的IP网段&#xff0c;数据在不同的IP网段之间交互是需要借助三层设备的&#xff0c;这些设备具备路由能力&#xff0c;能够实现数据的跨网段转发。 路由是数据通信网络中最基…...