当前位置: 首页 > 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…...

网络编程(Modbus进阶)

思维导图 Modbus RTU&#xff08;先学一点理论&#xff09; 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议&#xff0c;由 Modicon 公司&#xff08;现施耐德电气&#xff09;于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误

HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误&#xff0c;它们的含义、原因和解决方法都有显著区别。以下是详细对比&#xff1a; 1. HTTP 406 (Not Acceptable) 含义&#xff1a; 客户端请求的内容类型与服务器支持的内容类型不匹…...

简易版抽奖活动的设计技术方案

1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来

一、破局&#xff1a;PCB行业的时代之问 在数字经济蓬勃发展的浪潮中&#xff0c;PCB&#xff08;印制电路板&#xff09;作为 “电子产品之母”&#xff0c;其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透&#xff0c;PCB行业面临着前所未有的挑战与机遇。产品迭代…...

day52 ResNet18 CBAM

在深度学习的旅程中&#xff0c;我们不断探索如何提升模型的性能。今天&#xff0c;我将分享我在 ResNet18 模型中插入 CBAM&#xff08;Convolutional Block Attention Module&#xff09;模块&#xff0c;并采用分阶段微调策略的实践过程。通过这个过程&#xff0c;我不仅提升…...

LeetCode - 394. 字符串解码

题目 394. 字符串解码 - 力扣&#xff08;LeetCode&#xff09; 思路 使用两个栈&#xff1a;一个存储重复次数&#xff0c;一个存储字符串 遍历输入字符串&#xff1a; 数字处理&#xff1a;遇到数字时&#xff0c;累积计算重复次数左括号处理&#xff1a;保存当前状态&a…...

【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力

引言&#xff1a; 在人工智能快速发展的浪潮中&#xff0c;快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型&#xff08;LLM&#xff09;。该模型代表着该领域的重大突破&#xff0c;通过独特方式融合思考与非思考…...

MVC 数据库

MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...

TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案

一、TRS收益互换的本质与业务逻辑 &#xff08;一&#xff09;概念解析 TRS&#xff08;Total Return Swap&#xff09;收益互换是一种金融衍生工具&#xff0c;指交易双方约定在未来一定期限内&#xff0c;基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...

Caliper 配置文件解析:config.yaml

Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...