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

【华为OD机试模拟题】用 C++ 实现 - 找到它(2023.Q1)

最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 去重求和(2023.Q1) 文章目录 最近更新的博客使用说明找到它题目输入输出示例一输入输出示例二输入输出说明Code使用说明 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率才会高。 华为 OD …...

python中yield的使用

在 Python 中&#xff0c;yield 是一个关键字&#xff0c;它用于定义生成器函数。生成器函数是一个特殊的函数&#xff0c;可以返回一个迭代器&#xff0c;当生成器函数被调用时&#xff0c;它不会立即执行&#xff0c;而是返回一个生成器对象&#xff0c;通过迭代生成器对象可…...

GO进阶(4) 深入Go的内存管理

Go语言成为高生产力语言的原因之一自己管理内存&#xff1a;Go抛弃了C/C中的开发者管理内存的方式&#xff0c;实现了主动申请与主动释放管理&#xff0c;增加了逃逸分析和GC&#xff0c;将开发者从内存管理中释放出来&#xff0c;让开发者有更多的精力去关注软件设计&#xff…...

【C++】类与对象理解和学习(下)

放在专栏【C知识总结】&#xff0c;会持续更新&#xff0c;期待支持&#x1f339;建议先看完【C】类与对象理解和学习&#xff08;上&#xff09;【C】类与对象理解和学习&#xff08;中&#xff09;本章知识点概括Ⅰ本章知识点概括Ⅱ初始化列表前言在上一篇文章中&#xff0c;…...

【Neo4j】Spring Data Neo4j APi阅读随笔

引言 关于Spring boot整合Neo4j的官方api翻译&学习随笔 (TOC) 一、准备工作 1.注入依赖 <dependency><groupId>org.springframework.data</groupId><artifactId>spring-data-jpa</artifactId></dependency>2.配置yml文件 这里是本…...

JVM内存模型简介

1 程序计数器 程序计数器是一块较小的内存空间&#xff0c;可以看作是当前线程所执行的字节码的行号指示器。字节码解释器工作时通过改变这个计数器的值来选取下一条需要执行的字节码指令&#xff0c;分支、循环、跳转、异常处理、线程恢复等功能都需要依赖这个计数器来完。 ja…...

k8s如何给node添加标签

一、为什么需要标签&#xff1f; k8s集群如果由大量节点组成&#xff0c;可将节点打上对应的标签&#xff0c;然后通过标签进行筛选及查看,更好的进行资源对象的相关选择与匹配 二、怎么查看目前node上具有的标签 [rootmaster01 ~]# kubectl get node --show-labels NAME …...

【大数据Hive】Hive ddl语法使用详解

一、前言 使用过关系型数据库mysql的同学对mysql的ddl语法应该不陌生&#xff0c;使用ddl语言来创建数据库中的表、索引、视图、存储过程、触发器等&#xff0c;hive中也提供了类似ddl的语法。本篇将详细讲述hive中ddl的使用。 二、hive - ddl 整体概述 在Hive中&#xff0c;DA…...

Connext DDS录制服务 Recording Service(2)

2.4 远程管理 控制客户端(如RTI管理控制台)可以使用此接口远程控制录制服务。 注:记录服务远程管理基于第10.3节中描述的RTI远程管理平台。有关录制服务中远程管理工作的详细讨论,请参阅该手册 下面是所有支持操作的API引用。 2.4.1 启用远程管理 默认情况下,在录制服务中…...

mysql数据类型选择

数据类型选择 完整性约束 是完整性约束是为保证数据库中数据的正确性和相容性&#xff0c;对关系模型提出的某种约束条件或规则。 通常包括&#xff1a;实体完整性约束、参照完整性约束、域完整性约束、用户自定义完整性约束。 实体完整性(Entity integrity)是指主键必须非空…...