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

大数据学习栈记——Neo4j的安装与使用

本文介绍图数据库Neofj的安装与使用&#xff0c;操作系统&#xff1a;Ubuntu24.04&#xff0c;Neofj版本&#xff1a;2025.04.0。 Apt安装 Neofj可以进行官网安装&#xff1a;Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...

python打卡day49

知识点回顾&#xff1a; 通道注意力模块复习空间注意力模块CBAM的定义 作业&#xff1a;尝试对今天的模型检查参数数目&#xff0c;并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...

【Oracle APEX开发小技巧12】

有如下需求&#xff1a; 有一个问题反馈页面&#xff0c;要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据&#xff0c;方便管理员及时处理反馈。 我的方法&#xff1a;直接将逻辑写在SQL中&#xff0c;这样可以直接在页面展示 完整代码&#xff1a; SELECTSF.FE…...

.Net框架,除了EF还有很多很多......

文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...

通过Wrangler CLI在worker中创建数据库和表

官方使用文档&#xff1a;Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后&#xff0c;会在本地和远程创建数据库&#xff1a; npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库&#xff1a; 现在&#xff0c;您的Cloudfla…...

GitHub 趋势日报 (2025年06月08日)

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...

NLP学习路线图(二十三):长短期记忆网络(LSTM)

在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...

k8s业务程序联调工具-KtConnect

概述 原理 工具作用是建立了一个从本地到集群的单向VPN&#xff0c;根据VPN原理&#xff0c;打通两个内网必然需要借助一个公共中继节点&#xff0c;ktconnect工具巧妙的利用k8s原生的portforward能力&#xff0c;简化了建立连接的过程&#xff0c;apiserver间接起到了中继节…...

全志A40i android7.1 调试信息打印串口由uart0改为uart3

一&#xff0c;概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本&#xff1a;2014.07&#xff1b; Kernel版本&#xff1a;Linux-3.10&#xff1b; 二&#xff0c;Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01)&#xff0c;并让boo…...

selenium学习实战【Python爬虫】

selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...