运用qsort函数进行快排并使用C语言模拟qsort

qsort 函数的使用

       首先qsort函数是使用快速排序算法来进行排序的,下面我们打开官网来查看qsort是如何使用的。


这里有四个参数,首先base 是至待排序的数组的首元素的地址,num 是值这个数组的元素个数,size 是指每个元素的大小,最后是一个函数指针(用来比较两个元素的不同,其中这个函数需要有返回值,当返回值小于0时p1需要放在p2前面,等于0时p1和p2不用改变位置,当返回值大于0时,p1需要放在p2的后面)由此可见,这个函数需要我们自己去编写,然后通过函数指针来调用。

下面我们来看看qsort的实践效果:

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

typedef struct Stu
{
	char name[20];
	int age;
}Stu;

int cmp_array(const void* p1,const void* p2)
{
	return *(int*)p1 - *(int*)p2;
}

int cmp_stu_name(const void* p1, const void* p2)
{
	return strcmp(((Stu*)p1)->name, ((Stu*)p2)->name);
}

int cmp_stu_age(const void* p1, const void* p2)
{
	return ((Stu*)p1)->age - ((Stu*)p2)->age;
}

void PrintArray(int arr[], int sz)
{
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}

void PrintStu(Stu* s, int sz)
{
	for (int i = 0; i < sz; i++)
	{
		printf("%s %d", s[i].name, s[i].age);
		printf("\n");
	}
}

int main()
{
	Stu s[3] = { {"zhangsan",18},{"lisi",19},{"sunwu",15} };
	int sz = sizeof(s) / sizeof(s[0]);

	int arr[10] = { 4,8,5,7,9,6,2,0,1,3 };
	int sz2 = sizeof(arr) / sizeof(arr[0]);

	qsort(s, sz, sizeof(s[0]), cmp_stu_name);
	PrintStu(s, sz);
	printf("\n");

	qsort(s, sz, sizeof(s[0]), cmp_stu_age);
	PrintStu(s, sz);
	printf("\n");

	qsort(arr, sz2, sizeof(arr[0]), cmp_array);
	PrintArray(arr, sz2);
	printf("\n");

	return 0;
}

使用C语言模拟qsort

       这里我们使用冒泡排序算法进行排序,使用C语言来模拟qsort函数。
       首先我们来回顾冒泡排序算法,有两个要点一个是排序的趟数,另一个是每一趟排序的次数。这里以升序为例:

void bubble_sort(int arr[], int sz)
{
	for (int i = 0; i < sz - 1; i++)
	{
		for (int j = 0; j < sz - 1 - i; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
}

然后来模拟qsort函数呢?首先qsort函数几乎对任何数据都可以排序,所以我们的bubble_sort函数要做出相应调整,然后设计形参呢?对任何数据进行排序,也就是说数据的类型和大小都是不确定的,这样的话,我们可以使用size_t来作为数据类型,用void来接收不同类型的指针,实在不会的,我们可以参考qsort 函数来设计的。

void
base 接收待排序的首元素的地址,size_t num 和 size_t size 来接收元素个数和元素大小,最后就是最重要的函数设计了。

void bubble_sort(void* base, size_t num, size_t size, int (*compar) (const void* p1, const void* p2))

设计好形参,我们来考虑一下函数的主体部分,首先趟数是不改变的,每趟的次数也不用改变,毕竟我们还是使用冒泡排序算法,这样的话,还有最后一个就是if这个判断语句,应为我们无法直接通过像上面一样对两个数进行直接比较,我们需要调用函数来进行比较,也就是compar函数。

那有个问题,我们如何来写compar 函数的指针呢?这个指针不能大也不能小,否则就无法准确比较或者会产生越界行为,这样我们可以使用char* 为什么呢?首先我们需要两个两个数据来进行一一比较,这样我们需要知道准确的地址,必须是恰好指向每个元素的地址,而char 刚好就是一个字节,只要准确地进行指针加法运算就能得到这个元素地地址。

if (compar((char*)base + j * size, (char*)base + (j + 1) * size) > 0)

还有个问题,我们怎么交换数据呢?其实和上面的理由差不多由于数据的类型不同,他们的大小也不同。这时我们可以使用char 因为char 是最小的数据类型了,也就是一个字节,无论数据是几个字节,都是char 的倍数也就是说都可以用一个字节的倍数来表示,这样的话,我们只需要知道数据类型的大小(size) 就可以来通过循环遍历来一个字节一个字节来进行进行交换就可以了,我们可以封装一个函数swap。

void swap(char* p1, char* p2, size_t size)
{
	int i = 0;
	while (i < size)
	{
		char tmp = *(p1 + i);
		*(p1 + i) = *(p2 + i);
		*(p2 + i) = tmp;
		i++;
	}
}

那么我们最后得到的bubble_sort函数如下:

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

我们来演练一下,看看效果是不是和qsort有着一样的效果:

代码如下:

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

typedef struct Stu
{
	char name[20];
	int age;
}Stu;

int cmp_array(const void* p1,const void* p2)
{
	return *(int*)p1 - *(int*)p2;
}

int cmp_stu_name(const void* p1, const void* p2)
{
	return strcmp(((Stu*)p1)->name, ((Stu*)p2)->name);
}

int cmp_stu_age(const void* p1, const void* p2)
{
	return ((Stu*)p1)->age - ((Stu*)p2)->age;
}

void PrintArray(int arr[], int sz)
{
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}

void PrintStu(Stu* s, int sz)
{
	for (int i = 0; i < sz; i++)
	{
		printf("%s %d", s[i].name, s[i].age);
		printf("\n");
	}
}

void swap(char* p1, char* p2, size_t size)
{
	int i = 0;
	while (i < size)
	{
		char tmp = *(p1 + i);
		*(p1 + i) = *(p2 + i);
		*(p2 + i) = tmp;
		i++;
	}
}

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

int main()
{
	Stu s[3] = { {"zhangsan",18},{"lisi",19},{"sunwu",15} };
	int sz = sizeof(s) / sizeof(s[0]);

	int arr[10] = { 4,8,5,7,9,6,2,0,1,3 };
	int sz2 = sizeof(arr) / sizeof(arr[0]);

	bubble_sort(s, sz, sizeof(s[0]), cmp_stu_name);
	PrintStu(s, sz);
	printf("\n");

	bubble_sort(s, sz, sizeof(s[0]), cmp_stu_age);
	PrintStu(s, sz);
	printf("\n");

	bubble_sort(arr, sz2, sizeof(arr[0]), cmp_array);
	PrintArray(arr, sz2);
	printf("\n");

	return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数字化转型导师坚鹏:证券公司数字化转型战略、方法与案例

证券公司数字化转型战略、方法与案例 课程背景&#xff1a; 数字化转型背景下&#xff0c;很多机构存在以下问题&#xff1a; 不清楚证券公司数字化转型的发展战略&#xff1f; 不知道证券公司数字化转型的核心方法&#xff1f; 不知道证券公司数字化转型的成功案例&am…

第四十八回 解珍解宝双越狱 孙立孙新大劫牢-Python模块和包概念与使用

吴用对宋江说&#xff0c;有个人&#xff0c;他是石勇的关系&#xff0c;与祝家庄的峦廷玉关系好&#xff0c;还是杨林、邓飞的老相识&#xff0c;他有一计.... 原来在宋江攻打祝家庄的时间段&#xff0c;山东海边登州也发生了一件事。登州山下有一家猎户&#xff0c;弟兄两个…

Linux下进程相关概念详解

目录 一、操作系统 概念 设计操作系统的目的 定位 如何理解“管理” 系统调用和库函数概念 二、进程 概念 描述进程—PCB&#xff08;process control block&#xff09; 查看进程 进程状态 进程优先级 三、其它的进程概念 一、操作系统 概念 任何计算机系统都包…

HPE ProLiant MicroServer Gen8更换坏硬盘(RAID 1+0)

HPE ProLiant MicroServer Gen8今天硬盘告警&#xff0c;坏了一块硬盘&#xff08;估计还是由于上次突然断电导致的&#xff09;&#xff0c;关机&#xff0c;拆下坏硬盘&#xff0c;更换新硬盘&#xff0c;开机后按了一次F1键&#xff0c;系统继续启动并正常使用&#xff0c;同…

VueCli的安装与卸载

文章目录 一.Node安装包的报读网盘提取码二、Vue脚手架Cli三、Vue-CLI使用步骤(自定义安装)1.转换路径并创建项目2.创建步骤的解释(保姆级)3.等待vue项目自己创建好(保姆级) 四、通过npm对vue的安装与卸载 一.Node安装包的报读网盘提取码 下面的链接为地址: Node的百度网盘提取…

面试准备:排序算法大汇总 C++

排序算法总结 直接插入排序 取出未排序部分的第一个元素&#xff0c;与已排序的部分从后往前比较&#xff0c;找到合适的位置。将大于它的已排序的元素向后移动&#xff0c;将该元素插入到合适的位置。 //1. 直接插入排序 void InsertionSort(vector<int>& nums){f…

#WEB前端(HTML属性)

1.实验&#xff1a;a,img 2.IDE&#xff1a;VSCODE 3.记录&#xff1a; a: href插入超链接 默认情况下在本窗口打开链接, target可以设置打开的窗口,parent在父窗口打开&#xff0c;blank新开串口打开,top在顶层串口打开,self为默认在本窗口打开 img: 插入图片 可以插…

走进SQL审计视图——《OceanBase诊断系列》之二

1. 前言 在SQL性能诊断上&#xff0c;OceanBase有一个非常实用的功能 —— SQL审计视图(gv$sql_audit)。在OceanBase 4.0.0及更高版本中&#xff0c;该功能是 gv$ob_sql_audit。它可以使开发和运维人员更方便地排查在OceanBase上运行过的任意一条SQL&#xff0c;无论这些SQL是成…

基于 Amazon EKS 的 Stable Diffusion ComfyUI 部署方案

01 背景介绍 Stable Diffusion 作为当下最流行的开源 AI 图像生成模型在游戏行业有着广泛的应用实践&#xff0c;无论是 ToC 面向玩家的游戏社区场景&#xff0c;还是 ToB 面向游戏工作室的美术制作场景&#xff0c;都可以发挥很大的价值&#xff0c;如何更好地使用 Stable Dif…

Day10:基础入门-HTTP数据包Postman构造请求方法请求头修改状态码判断

目录 数据-方法&头部&状态码 案例-文件探针 案例-登录爆破 工具-Postman自构造使用 思维导图 章节知识点&#xff1a; 应用架构&#xff1a;Web/APP/云应用/三方服务/负载均衡等 安全产品&#xff1a;CDN/WAF/IDS/IPS/蜜罐/防火墙/杀毒等 渗透命令&#xff1a;文件…

数字图像处理 SUJOY滤波器:用于图像边缘检测的通用一阶导数滤波器

1、前言 因为是比较旧的论文,但是据论文作者说SUJOY滤波器为图像边缘检测提供了比其他常用的一阶导数方法(如 Robert 算子、Prewitt 算子、Sobel 算子等)更好的方法。 经过测试感觉并没有作者说的那么好,很水的东西,另外图像领域的事情很少有绝对的,通常都是某些方法适合…

【二分】第k个缺失的数

第K个缺失的数 链接 . - 力扣&#xff08;LeetCode&#xff09;. - 备战技术面试&#xff1f;力扣提供海量技术面试资源&#xff0c;帮助你高效提升编程技能,轻松拿下世界 IT 名企 Dream Offer。https://leetcode.cn/problems/kth-missing-positive-number/ 题目 题解 二段…

医学大数据|文献阅读|有关“胃癌+机器学习”的研究记录

目录 1.基于32基因特征构建的机器学习模型可有效预测胃癌患者的预后和治疗反应 2.胃癌患者术后90天死亡率的机器学习风险预测模型 3.使用机器学习模型预测幽门螺杆菌根除患者胃癌患病风险 4.利用初始内窥镜检查和组织学结果进行个性化胃癌发病率预测 1.基于32基因特征构建的…

ABAP - SALV教程07 斑马纹显示和SALV标题

SALV设置斑马纹和标题 METHOD set_layout.DATA: lo_display TYPE REF TO cl_salv_display_settings. * 取得显示对象lo_display co_alv->get_display_settings( ).* 设置ZEBRA显示lo_display->set_striped_pattern( X ). * 设置Titlelo_display->set_list_he…

探讨倒排索引Elasticsearch面试与实战:从理论到实践

在当前大数据时代&#xff0c;Elasticsearch&#xff08;以下简称为ES&#xff09;作为一种强大的搜索和分析引擎&#xff0c;受到了越来越多企业的青睐。因此&#xff0c;对于工程师来说&#xff0c;掌握ES的面试准备和实战经验成为了必备技能之一。本文将从ES的面试准备和实际…

【MCAL】TC397+EB-tresos之CAN配置实战 - (CAN/CANFD)

本篇文章介绍了在TC397平台使用EB-tresos对CAN驱动模块进行配置的实战过程,不仅介绍了标准CAN的发送与接收&#xff0c;还介绍了CANFD的实现与调试以及扩展帧的使用。M_CAN是德国博世公司开发的IP&#xff0c;因为英飞凌的芯片完整的集成了这个IP&#xff0c;所以整体的配置都比…

leetcode 热题 100_三数之和

题解一&#xff1a; 双指针遍历&#xff1a;暴力解法的三层遍历会超时&#xff0c;因此需要优化遍历的过程。首先是需要对结果进行去重&#xff0c;这里采用排序跳过重复值的做法&#xff0c;在指针遍历时跳过已经遍历过的相同值。在第一层循环确定第一个值后&#xff0c;剩下两…

【RT-Thread基础教程】邮箱的使用

文章目录 前言一、邮箱的特性二、邮箱操作函数2.1 创建邮箱创建动态邮箱创建静态邮箱 2.2 删除邮箱2.3 发邮件2.4 取邮件 三、示例代码总结 前言 RT-Thread是一个开源的实时嵌入式操作系统&#xff0c;广泛应用于各种嵌入式系统和物联网设备。在RT-Thread中&#xff0c;邮箱是…

阶跃信号与冲击信号

奇异信号&#xff1a;信号与系统分析中&#xff0c;经常遇到函数本身有不连续点&#xff08;跳变电&#xff09;或其导函数与积分有不连续点的情况&#xff0c;这类函数称为奇异函数或奇异信号&#xff0c;也称之为突变信号。以下为一些常见奇异函数。 奇异信号 单位斜变信号 …

java-ssm-jsp-宠物护理预定系统

java-ssm-jsp-宠物护理预定系统 获取源码——》公主号&#xff1a;计算机专业毕设大全