C语言标准库函数qsort( )——数据排序

2023-11-03

  

大家好!我是保护小周ღ,本期为大家带来的是深度解剖C语言标准库函数 qsort(),qsort()函数他可以对任意类型的数据排序,博主会详细解释函数使用方法,以及使用快速排序的左右指针法模拟实现函数功能这样的排序确定不来学习一下吗???

 

8b354a7916f240d0bc7839822301ba90.gif#pic_center

目录

一、qsort()函数简介

二、qsort() 函数的参数

三、qsort() 函数的使用

3.1 对整型数据排序 

3.2 对结构体类型数据排序

 四、快速排模拟实现qsort()函数


一、qsort()函数简介

qsort() 函数是C语言标准库提供的排序函数,q==Quick,函数内部是以快速排序的思想实现的,那qsort() 函数的意义是什么呢?内部居然还使用别的排序的思想。因为常规排序是写死的,假如原先是对整型数据的排序,现在要对结构体类型的数据排序,则需要修改函数参数,函数内部数据也要相应的修改。而qsort()函数他可以对任意类型的数据排序,比如说,可以直接排整型数据,也可以排结构体类型的数据,甚至是字符串数据,通用性极强。这样的排序确定不来学习一下吗???


二、qsort() 函数的参数

很明显 qsort()函数具有4个参数,接下来博主来解剖一下这些参数代表着什么意思。

1.   void * base : 首先来了解一下什么是 void* ,这个是无具体类型的指针,void * 的指针是非常宽容的,可以接收任意类型的数据。常常用来临时存放数据,等到需要使用数据时,我们必须要强制类型转换成某一具体类型的数据,才能对数据进行操作。

对void *pa,接收了一个整型 a 的地址,我们对指针pa 进行强制类型转换(int*),再解引用 pa即可对变量a 进行操作。

 void *base 存的就是待排序数据的起始地址(不能直接访问)。

 这个参数是 qsort() 函数能够对任意数据排序的基础。 

2. size_t  num : 记录待排序数据元素个数。

3. size_t  size  : 记录待排序数据任意一个元素的所占的字节数(元素的大小)。

4. int  (*compar) (const  void*  , const  void* ) : 

这其实是一个函数类型的指针,可以用来存储函数的地址,然后也提前声明了函数的参数,返回值

返回值是 int 类型,参数部分是两个 void * 类型的接收。这个函数的作用是来比较两个参数的大小,然后返回比较果结,怎么比呢? 如果是整型数据使两个参数相减,返回结果。如果是字符串,我们可以使用   strcmp(“字符串”,“字符串”);strcmp 函数的返回值也是整型数据(这个是根据对应的场景,选择比较方式),即可得到相应的结果。

这第四个参数需要我们自己设计实现,函数的作用就是比较任意两个参数,返回一个整型数据,就可以利用这个数据来判断两个元素大小,所以这是个比较排序。


三、qsort() 函数的使用

qsort()函数包含在 stdlib.h库里,所以我在使用前需要申明一下。

3.1 对整型数据排序 

#include<stdio.h>
#include<stdlib.h>//头文件声明

//判断两个元素的大小
int Compar(void const *p1,void const *p2)//无具体类型的指针接收数据,使用时强制类型转换。
{
    //两个整型数据相减,即可即可得到三种信息>,<,=
    return (*(int*)p1 - *(int*)p2) *(-1); //利用乘-1改变符号控制升序还是降序
}

//打印
void Print(int *arr,int n)
{
    for (int i=0;i<n;++i)
    {
        printf("%d ",arr[i]);
    }
}

int main()
{
    int arr[] = { 8,6,4,2,3,7,1,2,3,10,9 };
    int len = sizeof(arr) / sizeof(arr[0]);//记录数组元素个数
    int size = sizeof(arr[0]);//记录某一个元素的大小所占的字节数

    //库函数
    qsort(arr, len, size, Compar);
    //第四个参数,直接传函数的地址,函数名代表函数的地址,由函数指针接收。

    //打印
    Print(arr, len);
    return 0;
}


3.2 对结构体类型数据排序

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

typedef struct Student//定义一个Student类型的结构体
{
	char name[12]; //姓名
	int age;       //年龄
	char StuID[8]; //学号
}Student;//typedef,重命名一下,简化。

//比较任意两个元素的大小。
int CmpSort(const void* p1, const void * p2)
{
	//return ( ((Student*)p1)->age - ((Student*)p2)->age );//根据年龄来比

	return (strcmp(((Student*)p1)->name,((Student*)p2)->name));//按照姓名的首字母来比较
}

//打印
void Print(Student* ps,int n)
{
	for (int i=0;i<n;++i)
	{
		printf("%s %d %d\n",ps[i].name,ps[i].age,ps[i].StuID);
	}
}

int main()
{
	Student student[3] = { {"张三",18,"21933321"},{"李四",20,"21933323"},{"王老五",19,"21933322"}};

	int len = sizeof(student) / sizeof(student[0]);//统计Student 类型,student数组的元素的个数
	int size = sizeof(student[0]);//统计某一个Student 元素所占的字节数。
	//库函数
	qsort(student,len,size,CmpSort);

	Print(student,len);//打印
	
	return 0;
}


 四、快速排模拟实现qsort()函数

好了经过以上三节内容的铺垫,相信大家应该对 qsort() 函数的用法,功能明白了,接下来我们就要来模拟实现函数内部。上文说到排序思想是用快速排序的思想。那博主今天就用快速排序——左右指针法来模拟实现挖坑法有一点复杂(下文解释)。

老铁如果对快速排序的几种排序思想还有什么不明白的可以学习博主的专栏。文章最后会贴。

什么是左右指针法?一张图带你搞定:

利用递归来继续分割区间,分割后继续单趟排,最后实现整体排序,排序结束。

算法逻辑弄明白了,现在还有一个问题,那就是函数内部,不知道我们要对什么类型的数据进行排序,我们虽然使用 void * 无指定类型的指针来接收数据,内部怎么根据数据的类型来进行强制类型转换呢?不转换无法处理数据。

大家回忆一下,我们qsort()函数是不是有四个参数,其中有一个参数就是 某一个元素所占的字节大小。size。

我们是不是可以将 void * 转换成 char * ,char* 的指针每一次只能访问一个字节的内容。

举个例子:

现在访问数据元素的问题解决了,那怎么交换数据元素的位置呢?

还是建立在访问元素的基础上,先找到需要交换的元素的位置,再根据 size 的大小一个字节一个字节的交换数据。

//交换数据
void Swap(char* base1, char* base2, int size)
{
	for (int i = 0; i < size; ++i)//按字节转换
	{
		char tmp = *base1;
		*base1 = *base2;
		*base2 = tmp;
		++base1;
		++base2;	
	}
}

这一趟下来,两个元素的就实现了交换。

完整版代码:

#include <stdio.h>
#include <stdlib.h>

int Cmp_qsort(void const* p1, void const* p2)//用户输入,
{
	int size1 = (*(int*)p1 - *(int*)p2);
	return size1;
}

//交换数据
void Swap(char* base1, char* base2, int size)
{
	for (int i = 0; i < size; ++i)//按字节转换
	{
		char tmp = *base1;
		*base1 = *base2;
		*base2 = tmp;
		++base1;
		++base2;	
	}
}

//模拟实现
void _Quick_qsort(void const* base, int left, int right, int size, int(*Cmp_qsort)(void const* p1, void const* p2))
{
	if (left >= right)
	{
		return;
	}

	int begin = left;
	int end = right;
	int key = begin;//记录坑位的下标、

	while (begin < end)
	{

		while (begin < end && (Cmp_qsort((char*)base+ key*size, (char*)base + end * size) <= 0))
			--end;

		while (begin < end && (Cmp_qsort((char*)base+ key*size, (char*)base + begin * size) >= 0))
			++begin;
		Swap((char*)base +begin * size, (char*)base+end*size, size);

	}
	Swap((char*)base + begin * size, (char*)base + key * size, size);

	_Quick_qsort(base, left, begin - 1, size, Cmp_qsort);
	_Quick_qsort(base, begin + 1, right, size, Cmp_qsort);

}


//过渡一下
void Quick_qsort(void const* base, int len, int size,int(*Cmp_qsort)(void const *p1,void const *p2))
{
	_Quick_qsort(base, 0, len - 1, size, Cmp_qsort);//左右区间写入参数,
}

//打印
void Print(int* a, int n)
{
	for (int i = 0; i < n; ++i)
	{
		printf("%d ", a[i]);
	}
}

//快排左右指针法
int main()
{
	int a[] = {6,1,2,10,7,9,9,3,4,5,10,8};
	int len = sizeof(a) / sizeof(a[0]);
	int size = sizeof(a[0]);

	Quick_qsort(a, len, size,Cmp_qsort);//快速排序模拟实现

	Print(a, len);//打印
	return 0;

}

这个模拟是争对于顺序结构的数据,而链式结构要采用不同的办法。

不采用快排的挖坑发的原因是:我们需要一个类型大小的空间存坑值,这个时候我们只能根据size(一个元素所占的字节数)动态开辟一个char *的数组,一个字节一个字节的存储。如果光定义指针指向,那坑值指针,会随着指向地址的元素的变化而变化。 


至此,C语言的深度解剖 qsort() 函数,博主已经分享完了,希望对大家有所帮助,如有不妥之处欢迎批评指正。

 

本期收录于博主的专栏——数据排序,适用于编程初学者,感兴趣的朋友们可以订阅,查看其它“经典数据排序”。排序算法_保护小周ღ的博客

感谢每一个观看本篇文章的朋友,更多精彩敬请期待:保护小周ღ  *★,°*:.☆( ̄▽ ̄)/$:*.°★* 

文章存在借鉴,如有侵权请联系修改删除! ​​

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C语言标准库函数qsort( )——数据排序 的相关文章

随机推荐

  • 储存过程之拉链表

    很多做数仓的朋友在面试的时候都会被问到 你写过的最难的存储过程是什么 这时候我们都会想到拉链表 拉链表真的那么难吗 下面我简单介绍一下拉链表作用 以及制作思路 希望可以帮到你 拉链表的作用 数据进行增量或者全量同步时 我们希望保留少数字段历
  • ELF文件格式的详解

    1 说明 2 elf文件的基本格式 3 elf文件的头部信息 4 elf文件的节区 Section 4 1 节区的作用 4 2 节区的组成 5 elf文件的段 Segment 6 用python解析elf文件 7 总结 1 说明 ELF的英
  • 设计模式之中介者模式

    中介者模式包装了一系列对象相互作用的方式 使得这些对象1不必相互明显作用 从而使它们可以松散耦合 当某些对象之间的作用发生改变时 不会立即影响到其他的一些对象之间的作用 保证这些作用可以彼此独立变化 中介者模式将多对多的相互作用转化为一对多
  • C语言函数程序实例(超全)

    目录 1 编写函数fun 功能是 计算n门课程的平均值 计算结果作为函数值返回 例如 若有5门课程的成绩是 92 76 69 58 88 则函数的值为76 599998 2 用函数将一个N阶方阵进行转置 输入输出在主函数中实现 使用for循
  • [QT编程系列-7]:C++图形用户界面编程,QT框架快速入门培训 - 3- QT窗体设计 - 自定义工具栏、状态栏、快捷键、图标

    目录 3 QT窗体设计 3 2 自定义工具栏 3 2 1 目标 3 2 2 实现过程 3 2 自定义状态栏 3 2 1 目标 3 2 2 过程 3 3 自定义动作快捷键 3 4 自定义图标 3 QT窗体设计 3 2 自定义工具栏 在Qt中
  • idea2023搜不到中文插件问题

    idea2023搜不到中文插件 可以考虑下载插件后本地安装 注意先看idea版本号 然后从官网下载离线汉化包 选择的版本等于或者低于idea版本号 1 离线汉化包下载地址 Chinese Simplified Language Pack 中
  • python操作excel(xlrd、xlwt、xlwings、pandas、openpyxl)

    文章目录 一 xlrd库 1 工作簿 book 1 创建工作簿对象 2 工作表 sheet 1 创建工作表对象 3 单元格 range 1 获取单个单元格的值 两种方法 2 获取单行或单列的值 4 获取工作表中的总行列数 二 xlwt库 1
  • JAVA多线程--信号量(Semaphore)

    简介 信号量 Semaphore 有时被称为信号灯 是在多线程环境下使用的一种设施 它负责协调各个线程 以保证它们能够正确 合理的使用公共资源 一个计数信号量 从概念上讲 信号量维护了一个许可集 如有必要 在许可可用前会阻塞每一个 acqu
  • VUE + 微信分享

    cnpm i weixin js sdk save 在需要分享的页面引入 import wx from weixin js sdk import shares from api index 在methods写调用的方法 vue调用微信的自定
  • Idea 代码存在报找不到符号

    前言 话说IDEA这个开发工具已经用了好几个春秋了 没想到最近很是淘气呢 打算撂挑子了 来咱们来整顿一下这淘气的货 问题 Error 191 80 java 找不到符号 符号 方法 getListByParamWithoutPage jav
  • 国家发改委发布《“互联网+”高效物流实施意见》

    国家发展改革委关于印发 互联网 高效物流实施意见 的通知 发改经贸 2016 1647号 国务院有关部委 直属单位 各省 自治区 直辖市及计划单列市人民政府 为贯彻落实 国务院关于积极推进 互联网 行动的指导意见 国发 2015 40号 发
  • #ifdef,#ifndef,#define,#endif解析(原)

    我们在看一些开源的源代码的时候 经常会看到如下情景 if defined PTHREADS defined NOTHREADS define STL PTHREADS endif if defined UITHREADS defined P
  • github上的adminlte下载太慢,下载失败解决办法

    不知道为什么github上的AdminLTE下载特别慢 一直失败 可以到码云上的镜像仓库去下载 码云急速下载
  • USB/UART/I2C/SPI等接口传输速率

    USB UART I2C SPI等接口传输速率 目录 USB总线 UART I2C总线 SPI总线 GPIO RK3399 FMC 参考 USB总线 USB1 1 低速模式 low speed 1 5Mbps 全速模式 full speed
  • CompletableFuture使用(五)

    异步任务交互方法applyToEither acceptEither和runAfterEither的使用介绍 1 applyToEither两个异步任务哪个先返回就处理哪个异步任务结果并返回CompletableFuture Complet
  • Flexera 2023 云状态报告解读

    导读 根据 Flexera 2023 云状态报告显示 经济问题不会减缓公有云市场的增长 FinOps 仍然处于应用多云战略的企业首要关注的问题 AWS 和 Azure 在全球市场仍处于领导地位 Flexera 根据对全球 750 位云决策者
  • iOS开发网络-HTTP协议

    一 URL 1 基本介绍 URL的全称是Uniform Resource Locator 统一资源定位符 通过1个URL 能找到互联网上唯一的1个资源 URL就是资源的地址 位置 互联网上的每个资源都有一个唯一的URL 2 URL中常见的协
  • 前端基础(四)- 数组和对象方法

    数组的方法 Array prototype reduce reduce 方法会对数组中的每个元素按序执行一个由您提供的 reducer 函数 每一次运行 reducer 会将先前元素的计算结果作为参数传入 最后将其结果汇总为单个返回值 第一
  • npm离线安装npm包的两种方法

    1 使用npm link 使用npm link 的方式是最常用的方法 具体做法是在联网机器上下载pm2的源码并安装好依赖 拷贝到离线服务器上 最后借助npm link将pm2链接到全局区域 首先 将pm2的源代码克隆下来 git clone
  • C语言标准库函数qsort( )——数据排序

    大家好 我是保护小周 本期为大家带来的是深度解剖C语言标准库函数 qsort qsort 函数他可以对任意类型的数据排序 博主会详细解释函数使用方法 以及使用快速排序的左右指针法模拟实现函数功能 这样的排序确定不来学习一下吗 目录 一 qs