模拟实现qsort函数(采用冒泡排序的方式)

news2025/1/25 4:40:12

前言:

之前我在C语言:指针详解【进阶】后篇中提到了qsort函数,qsort函数作为一个库函数,在我们日常的代码编写中可能会用到,在上面提到的文章中我们也进行使用了这个函数,大家也了解了一些这个函数的使用方法,但我们作为学习者,我们不仅要会用,还要知道这个qsort函数的原理,更要自己能够模拟实现一个qsort函数来,这样才能对这个函数有更深刻的理解。

这里我们再次回顾一下qsort函数的用法,我们不清楚的可以打开cplusplus.com的网站搜索一下qsort函数 进行查看
引文原版:
在这里插入图片描述


中文网页翻译:(尽量看原版更准确哦)
在这里插入图片描述
从引文来看,qsort函数是一个可以排列任意类型数据的函数。
我们先从这个函数的参数来看:
在这里插入图片描述

这个函数一共有四个参数,一个void*类型的指针,两个size_t类型的整型,一个返回类型为int 、函数参数为两个void*类型的指针的函数指针。

这代表着在使用qsort函数时,我们需要知道要排序的第一个元素的地址,要排序元素的个数,每个元素的大小,以及一个能比较两个元素的大小的函数。

注:

size_t 是无符号整型

这里我们先简单使用一下这个函数:(记得引用头文件)

#include <stdio.h>
#include <stdlib.h>
int cmp_int(const int* p1, const int* p2)
{
	return *p1 - *p2;
}
int main()
{
	int arr[10] = { 7,6,1,2,8,9,3,5,0,4 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(int), cmp_int);
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

在这里插入图片描述


这个排序的原理可以类比一下一个简单的排序方法:冒泡排序。它与冒泡排序的底层排序思想可以看作是大致相似的。
这里简单实现一下冒泡排序:

#include <stdio.h>
void bubble_sort(int arr[], int sz)
{
	for (int i = 0; i < sz; i++)
	{
		int flag = 1;
		for (int j = 0; j < sz - i - 1; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
				flag = 0;
			}
		}
		//当flag等于1时,说明这一趟排序未发生交换,说明顺序已经排好了
		if (flag)
			break;
	}
}
int main()
{
	int arr[10] = { 7,6,1,2,8,9,3,5,0,4 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr, sz);
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

在这里插入图片描述

注:
由于qsort函数的真正底层排序思想是:快速排序的方法,这里我就先用冒泡排序来模拟实现一下这个函数,后面有机会,我也会把快速排序的模拟实现讲解出来的。


实现思路:

对于qsort函数的模拟实现来说,最难的部分是如何实现任意类型数据的排序,我们之前对于确定的数据进行排序时,往往可以用对应的方法进行比较两元素并进行交换来排序,但qsort函数在使用时,设计这个函数的人在编写这个函数时并不确定这个函数将来要排列什么类型的数据,所以他在设计函数参数时不能限定函数传参传过来的数据类型,所以要把第一个函数参数设计成一个void*类型的指针
现在函数已经把要排序的第一个元素的地址传过来了,那接下来还要传要排序的元素个数,来确定排序的次数。
同时,由于第一个参数是void*类型的,我们还要确定要排序元素的字节数,所以还要传每个元素的大小
最后,由于不同类型的元素的比较形式不同,我们需要用不同的方法来比较传过来的数据,但是函数设计并不能预见所有的排序情况,这就需要使用者在使用qsort函数时,自己编写一个能比较两个要传元素的大小的函数,再把这个函数的地址穿过来就行了


看到这里,我们大致清楚了qsort函数的设计思路,这里我们就简单的编写一个my_qsort函数来模拟实现一下这个函数吧。

在这里插入图片描述

这里我们对函数内容编写时要注意,我们传过来的元素数据时各种各样的,但我们的函数参数的接受时是void*的参数,我们如何知道我们访问几个字节就找到我们要排序的一个元素呢?
这时就需要使用到我们的第三个参数了,第三个参数接收的就是我们要排序的一个元素的大小(字节),我们只需要一次访问size个字节的数据就可以找到每个要排序的元素了,这时需要将base强制类型转换成char*类型的指针再乘以size就是完整的一个要排序的元素数据了。

在这里插入图片描述

现在我们需要进行相邻两个元素的比较,这里的比较就是使用者要传过来的函数指针,这里的比较函数要使用者自己编写

注意:设计的比较函数要和库里给定的该比较函数模板格式要一致。

同时,相邻元素比较完后,如果不符合顺序就需要交换相邻两元素了,这里我们再设计一个my_swap函数来进行交换:
对于要交换任意类型的数据,数据指针的接受也要设计成void*类型的,同时要传递数据的字节大小来找到整个数据,然后在函数内部用一层for循环来交换每个字节的数据就可以了。
代码实现:

void my_swap(void* p1, void* p2, size_t size)
{
	size_t i = 0;
	for (i = 0; i < size; i++)
	{
		char tmp = *((char*)p1 + i);
		*((char*)p1 + i) = *((char*)p2 + i);
		*((char*)p2 + i) = tmp;
	}
}

这样我们的my_qsort函数就设计完成了。


代码实现:

void my_swap(void* p1, void* p2, size_t size)
{
	size_t i = 0;
	for (i = 0; i < size; i++)
	{
		char tmp = *((char*)p1 + i);
		*((char*)p1 + i) = *((char*)p2 + i);
		*((char*)p2 + i) = tmp;
	}
}
void my_qsort(void* base, size_t num, size_t size, int (*cmp)(const void*, const void*))
{
	size_t i = 0;
	size_t j = 0;
	for (i = 0; i < num - 1; i++)
	{
		for (j = 0; j < num - i - 1; j++)
		{
			if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0)
			{
				my_swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
			}
		}
	}
}

这里就举两个示例来感受一下:
示例一:(排列整型数据)

#include <stdio.h>
int cmp_int(const int* p1, const int* p2)//比较函数
{
	return *p1 - *p2;
}
void my_swap(void* p1, void* p2, size_t size)
{
	size_t i = 0;
	for (i = 0; i < size; i++)
	{
		char tmp = *((char*)p1 + i);
		*((char*)p1 + i) = *((char*)p2 + i);
		*((char*)p2 + i) = tmp;
	}
}
void my_qsort(void* base, size_t num, size_t size, int (*cmp)(const void*, const void*))
{
	size_t i = 0;
	size_t j = 0;
	for (i = 0; i < num - 1; i++)
	{
		for (j = 0; j < num - i - 1; j++)
		{
			if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0)
			{
				my_swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
			}
		}
	}
}
int main()
{
	int arr[10] = { 7,6,1,2,8,9,3,5,0,4 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	my_qsort(arr, sz, sizeof(int), cmp_int);
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

在这里插入图片描述


示例二:(排序字符数据)

#include <stdio.h>
#include <string.h>
int cmp_char(const char* p1, const char* p2)
{
	return strcmp(p1, p2);
}
void my_swap(void* p1, void* p2, size_t size)
{
	size_t i = 0;
	for (i = 0; i < size; i++)
	{
		char tmp = *((char*)p1 + i);
		*((char*)p1 + i) = *((char*)p2 + i);
		*((char*)p2 + i) = tmp;
	}
}
void my_qsort(void* base, size_t num, size_t size, int (*cmp)(const void*, const void*))
{
	size_t i = 0;
	size_t j = 0;
	for (i = 0; i < num - 1; i++)
	{
		for (j = 0; j < num - i - 1; j++)
		{
			if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0)
			{
				my_swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
			}
		}
	}
}
int main()
{
	char arr[10] = {'c', 'f', 'w', 'a', 'd', 'k', 'o', 'z', 'e', 'n'};
	int sz = sizeof(arr) / sizeof(arr[0]);
	my_qsort(arr, sz, sizeof(char), cmp_char);
	for (int i = 0; i < sz; i++)
	{
		printf("%c ", arr[i]);
	}
	return 0;
}

在这里插入图片描述


写到这里本篇关于 qsort函数的模拟实现(冒泡排序) 的文章就到此结束了,对于这个函数的模拟实现,下来还是要多加练习才能更好的掌握。
下一篇我就正式进入对字符串的研究进行深入了解,并讲解一系列关键的字符串函数和内存函数,和它们的模拟实现。


感兴趣的的小伙伴点点赞,点点关注,谢谢大家的阅读哦!!!
精彩不容错过,点点关注,后期不错过哦!😘
你们的鼓励就是我的动力,欢迎下次继续阅读!!!😘😘😘

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/521494.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

English Learning - L3 作业打卡 Lesson1 Day4 2023.5.8 周一

English Learning - L3 作业打卡 Lesson1 Day4 2023.5.8 周一 引言&#x1f349;句1: They may say they are red hot about something unfair.成分划分弱读连读爆破语调 &#x1f349;句2: When they are red hot, they are very angry about something.成分划分弱读连读爆破语…

【Ansys】mechanical和fluent求解器中使用的迭代方法-待补充

一、mechanical求解器 这个求解器&#xff0c;在网上很容易查到&#xff0c;迭代求解时用的就是牛顿-拉夫逊方法&#xff08;Newton-Raphson&#xff09;。 这是因为牛顿法求解非线性问题非常优秀。 而mechanical使用这个方法就能实现对几何非线性、材料非线性、接触非线性、…

Java入门和背景知识

文章目录 &#x1f525;常见编程语言介绍&#x1f525;Java 发展史&#x1f525;Java 的核心优势&#x1f525;Java 各版本的含义&#x1f525;Java 的特性和优势&#x1f525;Java 运行机制&#x1f525;JVM、JRE 和 JDK&#x1f525;Java 开发环境搭建&#x1f525;openJDK 和…

网络购物商场系统的设计与实现(论文+源码)_kaic

网络购物商场系统 摘 要 近年来&#xff0c;随着网络购物的兴起和普及&#xff0c;针对该市场需求开发一款在线购物系统是大势所趋。和实体店对比&#xff0c;在线购物系统商品种类齐全&#xff0c;价格优惠、还能够送货上门等优势。在此类系统中&#xff0c;用户能够在网上…

潍坊这一城市商业综合体有奖征名

云创金谷项目商业购物中心名称及IP形象征集开始啦&#xff01;&#xff01;你有什么好想法&#xff1f;快来参与吧&#xff01;&#xff01; 云创金谷&#xff0c;是奎文区重点打造的城市更新代表力作&#xff0c;位于文化路以东、新华路以西&#xff0c;北宫街以北、卧龙东街以…

如何在vue中使用dayjs修改日历组件的星期名称

在vue中使用日历组件Calendar时&#xff0c;头部的星期默认展示为[日, 一, 二, 三, 四, 五, 六]&#xff0c;如下图所示。 如何改变头部的星期展示呢&#xff0c;可以通过以下方法实现&#xff1a; const weekdaysShort [周日, 周一, 周二, 周三, 周四, 周五, 周六]; dayjs.l…

每日学术速递5.10

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CV 1.ZipIt! Merging Models from Different Tasks without Training 标题&#xff1a;压缩它&#xff01;无需训练即可合并来自不同任务的模型 作者&#xff1a;George Stoica, Danie…

MLF(中期借贷便利)[Medium-term lending facility],俗称麻辣粉

MLF麻辣粉是什么&#xff1f;简述MLF的作用以及对股市的影响&#xff01;_哔哩哔哩_bilibili 简述 中期借贷便利&#xff08;Medium-term lending facility&#xff0c;简称MLF&#xff09;是中国人民银行提供给商业银行的一种货币政策工具&#xff0c;能够向商业银行提供一…

BFT 最前线 | 谷歌举办 I/O 23 发布会,对标必应,百度搜索小范围公测对话功能,盖茨称AI利大于弊

文 | BFT机器人 01 谷歌举办 I/O 23 发布会&#xff0c;以 AI 为核心发布一系列新工具 北京时间 5 月 11 日凌晨&#xff0c;谷歌举办了今年的 Google I/O 开发者大会。比起去年&#xff0c;强调「整合全球信息&#xff0c;使人人都能从访问中受益」的使命&#xff0c;谷歌 CEO…

xxl-job2.1.2定时任务使用教程

一、配置xxl-job页面调度器 1.先下载2.1.2版本xxl-job的源码&#xff0c;地址&#xff1a;https://github.com/xuxueli/xxl-job/tree/2.1.2 2.下载完&#xff0c;用idea打开&#xff0c;配置jdk1.8、配置maven&#xff0c; 3.导入MySQL数据库xxl-job的一些表&#xff0c;导入的…

点餐小程序实战教程02-店铺数据源设计

我们上一篇分析了点餐小程序的功能点,有了功能就好往下开发了。低代码开发是模型驱动开发,啥是模型驱动呢?就是要求你先设计好表结构,然后再开发页面。 那要如何设计数据源呢?其实就是将信息分门别类的拆分到不同的数据源中。要拆分到哪些数据源是由我们页面上的信息决定…

真题详解(索引查询)-软件设计(七十三)

外观、装饰、策略模式代码详解-软件设计&#xff08;七十二)https://blog.csdn.net/ke1ying/article/details/130628033 关于一个类的静态成员描述中&#xff0c;不正确的是&#xff1a; 错误&#xff1a;一个类的静态数据成员值不可以被修改。 java语言特性是有________和 垃…

django基础知识详解

1. 安装与介绍 课程特点&#xff1a; 学习难度大&#xff0c;大部分内容需要理解并记忆文件较多易混淆学习阶段注重框架使用&#xff0c;工作阶段注重实现业务逻辑综合应用强&#xff0c;小练习少 1.1 Django框架的介绍 2005年发布,采用Python语言编写的开源web框架早期的时…

JVM垃圾收集器(二)

目录 1、Serial 2、ParNew 3、Parallel Scavenge 4、Serial Old 5、Parallel Old 6、CMS 1、为什么需要两次“stop the world” 2、CMS的并发带来的问题 3、CMS的触发时机 4、CMS的缺陷 5、为什么CMS用清除算法 7、G1 1、Region 2、设计Region的意义 3、G1的三种…

【C++】——类与对象(中)+日期类对象的实现

文章目录 1. 前言2. 类的6个默认成员函数3. 构造函数4. 析构函数5. 拷贝构造函数6. 运算符重载6.1 赋值运算符重载 7. const成员8. 取地址及const取地址操作符重载9. 日期类对象的完整实现9.1 头文件9.2 源文件9.3 测试代码 10. 结尾 1. 前言 今天我们来继续学习C类与对象&…

网安学习路线!史上最详细没有之一

我经常会看到这一类的问题&#xff1a; 学习XXX知识没效果&#xff1b;学习XXX技能没方向&#xff1b;学习XXX没办法入门&#xff1b; 给大家一个忠告&#xff0c;如果你完全没有基础的话&#xff0c;前期最好不要盲目去找资料学习&#xff0c;因为大部分人把资料收集好之后&…

医日健自助售药机

产品概述 医日健智能自助售药机整合了信息化管理技术 、远程监控管理技术 、自动化技术、人脸识别技术等多种先进技术 &#xff0c;结合药品零售的特点 &#xff0c;通过在医院、诊所、药店、便利店、社区等场所部署药品自助售药机 &#xff0c;为用户提供自选购药服务&#x…

深度学习用于医学预后-第二课第三周1-3节-生存模型,生存函数

文章目录 生存模型生存函数有效生存函数 生存模型 本周&#xff0c;我们将讨论生存模式&#xff08;survival model&#xff09;。生存模型是一种特殊的模型我们关心事件发生的时间&#xff0c;比如从治疗到复发的时间&#xff0c;或者从诊断到死亡的时间 这是一个常见的问题…

GPT自动理解视频、法律顾问、大模型安全围栏

每天都要浏览大量AI相关新闻&#xff0c;是不是感到信息量爆炸&#xff0c;有效信息少&#xff0c;无从看起&#xff1f; 这么多新产品和新工具&#xff0c;到底哪些是真正是有价值的&#xff0c;哪些只是浮躁的热点&#xff1f; 想参与AI产品和工具的开发&#xff0c;但苦于…

READNE.md 语法

标题列表引用代码块链接图片分割线表格 1. 标题 #一级标题 ##二级标题 ###三级标题 ####四级标题 #####五级标题 ######六级标题 2. 列表 2.1 有序列表 直接在前面写数字序号&#xff1a; 1. a 2. bc 3. 1234 2.2 无序列表 有三种方式&#xff1a;""、"-&q…