【C语言】qsort 快速排序函数(详解+用法+my_qsort函数模拟实现)

2023-11-13

本文详细讲解qsort函数用法,并包含很多知识细节,干活满满!

qsort函数功能

排序是一个处理数据常用的功能,qsort(quick sort)快速排序就是八大排序算法之一,时间复杂度O(n)=nlogn。
qsort使用需要包含头文件<stdlib.h>,让qsort快排函数出彩的不只是它的排序速度,更是它几乎可以排序所有类型数组, 整型、字符型、浮点型,甚至根据结构体某个成员排序,不论升序降序, 都可以轻松实现。
接下来是qsort的用法,以及qsort是如何通过它的参数实现排序。

qsort函数声明

void qsort( void *base, 
			size_t num,
 			size_t width, 
 			int (*cmp )(const void *elem1, const void *elem2 ) );

上面代码是qsort库函数的声明:

  1. 函数是void类型,没有返回值。
  2. base是一个无类型指针,用来接收要被排序的数组首元素地址。void*可以指向任何类型的数据, 从函数参数我们就可以看出,qsort几乎可以排序所有类型。但是对于void*类型指针,我们要注意到一点:

void*类型的指针无法访问地址数据,这是因为指针压根就不知道它要访问多大空间,那么即使能访问得到的数据也毫无意义。因此不能对void*类型指针解引用操作,也不能做地址偏移±操作,这是语法型错误。

  1. num是size_t类型,用来接收数组要被排序的元素个数, 大家可以记住这个类型,**size_t是库用typedef对unsigned int类型命名的新名字,**所以num就是无符号整型。
  2. width也是size_t类型,用来接收数组一个元素的字节大小。
  3. cmp则是一个函数指针,这个函数返回值是int,有两个const修饰的void*型参数, 这个函数是由我们自己编写的,不过非常简单很容易实现。这也是qsort能排序很多类型的原因之一——它很灵活。 有的读者可能不清楚什么是函数指针,下面简要介绍一下。

函数指针

函数是如何被调用的呢,函数也有为它自己在栈区开辟的空间, 理所当然一个函数也有它自身的地址,就连main函数也不例外。
那么有了该函数地址我们就可以调用,或者传递该函数,也有了对应的函数指针来指向函数地址。 函数指针的类型当然是对应函数类型(包括返回类型和参数),下面举个简单例子:

#include <stdio.h>
//我们定义一个简单的返回较大值函数
int max(int a, int b)
{
	return a > b ? a : b;
}

int main()
{
	//函数名和数组名相似都代表地址
	//因为()的优先级高于*,所以要先把*和pf括起来,声明他是一个指针
	int (*pf)(int , int )=max;//也可以写成int(*pf)(int a,int b)=&max;
	//用max直接调用
	int c = max(10, 20);
	printf("max传递10,20后的c:%d\n", c);
	//用函数指针pf调用
	c = pf(10, 30);
	printf("pf传递10,30后的c:%d\n", c);

	return 0;
}

代码输出实例

qsort函数用法

qsort函数声明讲解完了,下面就是如何使用了。废话不多说直接上代码:
注意:cmp函数返回值大于0交换,小于等于0都不交换。

整型

#include <stdio.h>

int cmp(const void*e1,const void*e2)
{
	//因为无类型无法解引用,我们要根据需求强制类型转化,再解引用
	//e1是前一个元素,e2是后一个元素,返回值大于0交换,下面实现的是升序排序
	return *(int*)e1-*(int*)e2;//前比后大交换
	//如果要实现降序,从大到小,就应该写成
	//return *(int*)e2-*(int *)e1;后比前大交换
}

int main()
{
	int arr[10]={2,3,10,9,1,7,5,6,8,4};
	
	//这里我们想把arr升序排序,也就是从大到小排序
	//第一个参数是首元素地址,一般传的都是数组名
	//第二个参数是需要排序元素个数,一般直接填写,或借助sizeof计算
	//第三个参数是一个元素大小,直接用sizeof(arr[0])计算
	//第四个参数是我们编写的比较函数地址,注意此函数返回类型和参数类型是固定的,不能更改。
	qsort(arr,sizeof(arr)/sizeof(arr[0]),sizeof(arr[0]),cmp);
	
	for(int i=0;i<10;i++)
	{
	printf("%d ",arr[i]);
	}
	
	return 0;
}

代码输出实例

浮点型

对于不同类型的排序,主要是我们自己编写的cmp比较函数的修改:

//float类型升序排序
int cmp(const void*e1,const void *e2)
{
	//为了防止差值是零点几几,导致返回值是0,我们这样处理
	if((*(float*)e1-*(float*)e2)>0)
	{
		return 1;
	}
	//其它情况都不交换,我们直接返回0
	return 0;
}

//double类型升序排序
int cmp(const void*e1,const void *e2)
{
	if((*(double*)e1-*(double*)e2)>0)
	{
		return 1;
	}
	return 0;
}

字符型

想必通过上述两种类型举例,你已经可以写出字符型的cmp比较函数了。

int cmp(const void*e1,const void *e2)
{
	//字符类型升序排序
	//字符类型是特殊的整型,ASCII码值就是它的大小,可以直接作差
	return *(char*)e1-*(char*)e2;
}

字符串型

字符串类型比较大小不能直接作差,要借助strcmp函数根据字典序比较大小。

int cmp(const void*e1,const void *e2)
{
	//strcmp两个参数就是字符型指针,直接强制转换传参即可,
	//前大于后会返回值1,前等于后返回值0,前小于后返回值-1。
	return strcmp((char*)e1,(char*)e2);
}

结构体型

结构体排序看似很难,实际上理解清楚qsort用法十分简单。只需要把元素强制转换成结构体类型,再根据结构体某成员排序即可。
我们就拿学生举例,上完整代码:

#include <stdio.h>
#include <string.h>
struct student
{
	//我们要分别根据学生的名字升序,分数降序排序
	char name[20];
	int score;
}//名字升序排序函数
int name_cmp(const void *e1, const void *e2)
{
	//把元素强制转换成结构体类型,再根据结构体某成员排序
	return strcmp(((student *)e1)->name, ((student *)e2)->name);
}

//分数降序排序函数
int score_cmp(const void *e1, const void *e2)
{
	return ((student*)e2)->score-((student*)e1)->score;
}

int main()
{
	struct student arr[3]={{"zhangsan",80},{"lisi",92},{"wangwu",99}};
	
	//根据名字升序排序
	qsort(arr,sizeof(arr)/sizeof(arr[0]),sizeof(arr[0]),name_cmp);
	
	printf("名字升序排序后:\n");
	for(int i=0;i<3;i++)
	{
		printf("%s %d\n",arr[i].name,arr[i].score);
	}
	printf("\n");
	
	//根据分数降序排序
	qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(arr[0]), score_cmp);
	
	printf("分数降序排序后:\n");
	for(int i=0;i<3;i++)
	{
		printf("%s %d\n",arr[i].name,arr[i].score);
	}
	
	return 0;
}

代码输出实例

my_qsort函数模拟实现

学习完上面,你已经势不可挡了,大部分类型数组排序都可以轻松搞定。
下面我们更深入一步,用冒泡排序的方式,观察这些参数是如何巧妙地完成排序的:

void swap(char *str1, char *str2, int width)
{
	char tmp = 0;
	//对应交换一个元素大小width字节内容
	for (int i = 0; i < width; i++)
	{
		tmp = *(str1 + i);
		*(str1 + i) = *(str2 + i);
		*(str2 + i) = tmp;
	}
}

void my_qsort(void *arr, int num, int width, int (*cmp)(const void *, const void *))
{
	//冒泡排序框架
	for (int i = 0; i < num - 1; i++)
	{
		for (int j = 0; j < num - i - 1; j++)
		{
			//我们想比较两个元素大小,只需要把每个元素的首字节地址传递过去即可
			//cmp会帮我们实现类型转换比较,注意每个地址的偏移量不要写错
			if (cmp((char *)arr + j * width, (char *)arr + (j + 1) * width) > 0)
			{
				//满足交换条件了,那么只需要将这两个width大的空间内容对应交换
				//我们把交换独立封装为函数
				swap((char *)arr + j * width, (char *)arr + (j + 1) * width, width);
			}
		}
	}
}

int int_cmp(const void *e1, const void *e2)
{
	return *(int *)e1 - *(int *)e2;
}

int char_cmp(const void *e1, const void *e2)
{
	return *(char *)e1 - *(char *)e2;
}

int main()
{
	int darr[10] = { 1,2,4,6,9,10,3,8,7,5 };
	char carr[10+1] = "ahkgdefzpx";

	my_qsort(darr, 10, sizeof(int), int_cmp);
	my_qsort(carr, 10, sizeof(char), char_cmp);

	for (int i = 0; i < 10; i++)
	{
		printf("%d ",darr[i]);
	}
	printf("\n");

	printf("%s", carr);

	return 0;
}

代码输出实例


如果你能理解,恭喜你真的学会了个很NB的东西!
码字不容易,欢迎关注、点赞、收藏、评论、转发。

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

【C语言】qsort 快速排序函数(详解+用法+my_qsort函数模拟实现) 的相关文章

随机推荐

  • Java中JSON字符串和Java对象的互转

    1 JSON数据和Java对象的相互转换 JSON数据和Java对象的相互转换 JSON解析器 常见的解析器 Jsonlib Gson fastjson jackson 1 Java对象转换JSON 1 使用步骤 1 导入jackson的相
  • python中的库和模块有什么区别_Python中的模块和库之间有什么区别?

    从 The Python Tutorial Modules gt 模块 A module is a file containing Python definitions and statements The file name is the
  • 美团面试官问:写一个你认为最好的单例模式?于是我写了7个

    各位CSDN的朋友 如果喜欢我的文章 记得点个关注 方便以后找到我 由于是刚开始创作 推荐量较低 如果不关注 以后就可能找不到我了 面试题 写一个你认为最好的单例模式 面试考察点 考察目的 单例模式可以考察非常多的基础知识 因此对于这种问题
  • 软考-中级-网络工程师-知识点个人总结(九)

    1 CPU部件 运算器 控制器 寄存器 运算器 算术逻辑运算器 累加器 数据缓冲寄存器 状态条件寄存器 控制器 程序计数器 指令寄存器 指令译码器 时序部件 2 测试依据 测试类型 测试内容 测试依据 黑盒测试 功能测试 对软件的每个功能进
  • 小程序里的alert,Toast

    前言 在小程序中的弹框写法和我们在外面直接用是不一样的 他不支持alert 但是有替代的弹框组件 目录 一 原生小程序 原生小程序api 二 vant中的提示框 官网入口 vant api 一 原生小程序 wx showToast titl
  • C# MSChart 中柱状图和X轴自定义标签

    C 中MSChart 里面曲线 柱状图的样式选择是由 chart Series ChartType SeriesChartType Column 来控制的 SeriesChartType列举出了所有的样式 如果要在X轴上显示自己的文本标签
  • ChatGPT和代码智能

    一 ChatGPT 1 ChatGPT的自我介绍 2 ChatGPT的前世 2 1GPT 3是啥 General Pre Training GPT 即通用预训练语言模型 是一种利用Transformer作为特征抽取器 基于语言模型进行训练的
  • 深入理解Google Cast(三)探寻原理

    如何开发一个receiver application 先来简单说一下这个话题 Receiver本质就是一个网页 由html CSS和jacascript开发 如果要自定义receiver application 需要在 Google Cas
  • LaTeX的斜体,粗体

    参考 LaTeX的斜体 粗体 云 社区 腾讯云 写文章的小伙伴应该知道 在文章中 变量是需要斜体的 那么怎么才是斜体呢 首先 在LATEX中 强调可以以斜体形式展现出来 那么强调命令是如何体现的呢 语法 emph 内容 打开Winedit
  • 【翻译】ASF 法律委员会发布贡献者生成式 AI 指南

    中英文对照版请点击 ASF 法律委员会发布贡献者生成式 AI 指南 中英文对照 查看 除非你在过去一年左右的时间里一直生活在岩石之下 否则你很可能已经听说过很多关于生成式人工智能如何快速发展并正在迅速改变我们所熟知的软件行业的事情 虽然猜测
  • python 利用chinese_calendar 获取上一个工作日日期

    截止文章发布chinese calendar版本为1 8 0 大约在每年的11月份更新次年的节假日新版本 import datetime from chinese calendar import is workday def get per
  • 多线程下事务控制

    我篇文章 大数据批量新增or修改太慢太Low 线程池 CountDownLatch CompletableFuture完美解决 弊端就是无法实现事务控制 那么今天他就来啦 需求 大数据平台去获取数据 gt 通过对象组装 gt 插入到对应的表
  • Hibernate中merge()方法的坑

    标题Hibernate的merge方法的原理网上已经有很多篇文章介绍了 执行merge后 如果传入的对象有ID merge会先去数据库通过ID查 若查到则改 若查不到则增 也就是说 相比直接insert或是update 用merge的实现会
  • 执行kubeadm init 安装kubernetes时报错: [ERROR FileExisting-conntrack]: conntrack not found in system path

    使用kubeadin init安装kubernetes时报如下错误 解决方法 yum y install socat conntrack tools
  • STM32基于HAL库的开发与应用(2)GPIO口控制

    一 GPIO口是在单片机开发应用中使用最频繁的一个控制 GPIO口可作为输出高低电平也可以作为输入检测输入电平的高低 1 通常GPIO口输出控制LED灯 有源蜂鸣器等一些只需要高低电平就可以触发的模块 2 通常GPIO口作为输入 用来检测输
  • dz论坛伪静态加http跳转https遗留问题apache配置ssl

    一 首先 申请并且配置好服务器ssl证书 阿里 腾讯都有免费的 同时都有教程 下载apache格式的证书 解压后放到d ssl 目录 打开 D phpStudy Apache conf httpd conf 在最后面添加SSL配置
  • 2.基于原型的聚类方法

    基于原型的聚类方法 文章目录 一 概念 二 K Means 2 1 算法流程 2 2 超参数 2 3 特性 2 4 解析 2 5 K Means 2 6 Python实现 三 K Mediods 3 1 概念 3 2 算法对比 四 特性 一
  • android实现每天定时提醒的功能

    有时开发中有这样的需求 每天几点定时提醒等等 下面就来实现这个功能 首先新建一个广播接收者 public class AlarmReceiver extends BroadcastReceiver Override public void
  • 案例——UDP聊天

    UDP聊天案例 做一个网络编程相关的案例 想着用利用UDP的快速且不用连接的优点做一个聊天室 我们一个聊天程序需要可以接收消息 也要可以发送消息 所以我们的DatagramSocket对象不但需要调用send函数 还需要调用recieve函
  • 【C语言】qsort 快速排序函数(详解+用法+my_qsort函数模拟实现)

    本文详细讲解qsort函数用法 并包含很多知识细节 干活满满 文章目录 qsort函数功能 qsort函数声明 函数指针 qsort函数用法 整型 浮点型 字符型 字符串型 结构体型 my qsort函数模拟实现 qsort函数功能 排序是