模拟实现通用型排序

news2024/12/23 17:48:04

在这里插入图片描述

本期介绍🍖
主要介绍:什么是泛型排序,即:无类型排序,以及库函数qsort()的使用,以及如何自己模拟实现一个泛型的冒泡排序。


文章目录

  • 1. 什么是通用型排序
  • 2. 库函数qsort()
    • 2.1 定义
    • 2.2 使用
  • 3. 模拟实现通用类型的冒泡排序


1. 什么是通用型排序

  之前我们实现过冒泡排序,如下所示。但该排序只能对int型数组进行排序,因为在编写这个函数的时候,就是以排序整型来实现的。如若想排序其他类型的数据,只能重新实现排序函数。那有没有什么办法,让一个排序函数能够排序所有类型的数据呢?有的,那就是:通用类型排序

//冒泡排序
void bubble_sort(int arr[], int num)
{
	//趟数
	int i = 0;
	for (i = 0; i < num - 1; i++)
	{
		//每趟两两交换次数
		int j = 0;
		for (j = 0; j < num - 1 - i; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
}

2. 库函数qsort()

2.1 定义

  在C语言的库中就存在着一个函数qsort()快速排序,该排序就是上述的通用类型排序,使用前需要引用头文件stdlib.h。函数声明定义如下:

void qsort (void* base, size_t num, size_t size, 
			int(*cmp)(const void* p1, const void* p2));

  qsort()函数的返回类型为void,表示没有返回值,函数有4个参数,如下所示:

  1. base:是void*类型的指针,该指针指向待排序数组的起始位置。
  1. num:是待排序数组中元素的个数
  1. size:是待排序数组中每个元素的大小(单位:字节)
  1. cmp:是函数指针,指针的类型为int(*)(const void* p1, const void* p2)),该指针指向的函数返回类型为int,函数有两个参数p1和p2,都为泛型指针且类型都是const void*。该函数会被qsort()调用,用于比较两个元素的大小,并返回值,返回值的规则如下所示。

在这里插入图片描述

  如果p1指向的元素>p2指向的元素,那么返回一个大于0的数。如果p1指向的元素<p2指向的元素,那么返回一个小于0的数。如果p1指向的元素=p2指向的元素,那么返回值为0。


2.2 使用

  首先需要了解,qsort()函数的参数cmp指向的那个比较函数,是需要qsort()函数的调用方自己实现的。因为不同类型数据的比较方式是完全不一样的,就比如char、int、float、double可以使用>、<、=操作符比较大小,但字符串就不行,结构体也不行。所以只能是qsort()函数的调用方自己实现,然后将实现的比较函数的地址作为参数传给qsort()函数,qsort()函数在某个时机返回来调用比较函数,这就是回调函数的用法。qsort()函数实际使用如下:

  1. 排序整型数组
#include<stdio.h>
#include<stdlib.h>

void print(int* parr, size_t num)
{
	int i = 0;
	for (i = 0; i < num; i++)
	{
		printf("%d ", parr[i]);
	}
	printf("\n");
}

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

int main()
{
	int arr[] = { 3,1,4,5,7,2,9,8,0,6 };
	size_t num = sizeof(arr) / sizeof(arr[0]);
	size_t size = sizeof(arr[0]);
	qsort(arr, num, size, cmp_int);
	print(arr, num);
	return 0;
}

在这里插入图片描述

  1. 排序字符串
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

void print(char* arr[], size_t num)
{
	int i = 0;
	for (i = 0; i < num; i++)
	{
		printf("%s\n", arr[i]);
	}
}

int cmp_str(const void* p1, const void* p2)
{
	return strcmp(*(char**)p1, *(char**)p2);
}

int main()
{
	char* arr[] = { "zhangsan", "lisi", "wangwu"};
	size_t num = sizeof(arr) / sizeof(arr[0]);
	size_t size = sizeof(arr[0]);
	qsort(arr, num, size, cmp_str);
	print(arr, num);
	return 0;
}

在这里插入图片描述

  1. 排序结构体数组
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

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

void print(stu* parr, size_t num)
{
	int i = 0;
	for (i = 0; i < num; i++)
	{
		printf("%s %d\n", parr[i].name, parr[i].age);
	}
	printf("\n");
}

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

int main()
{
	stu arr[] = { {"zhangsan", 25},{"lisi", 38},{"wangwu", 17} };
	size_t num = sizeof(arr) / sizeof(arr[0]);
	size_t size = sizeof(arr[0]);
	qsort(arr, num, size, cmp_stu_age);
	print(arr, num);
	return 0;
}

在这里插入图片描述


3. 模拟实现通用类型的冒泡排序

  根据qsort()函数,模拟实现一个通用类型的冒泡排序。由于无法得知需要排序数组的类型,冒泡排序函数接受待排序数组的地址的类型就不确定,故只能通过泛型指针void*来接收。
  值得注意,void*类型的指针是无具体类型的,能够存放所有类型的地址,但无法对其进行解引用*和加减整数+、-操作。因为无法确定其指向元素的类型,所有对其进行解引用该访问多大空间不确定,加减整数操作步长是多大也不确定。冒泡函数的声明如下:

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

  问:为什么通用类型排序需要知道待排序数据元素的大小呢?带着这个问题接着往下看。
  通用类型的冒泡函数与原冒泡函数相比,排序的算法思路是不变的,依然是相邻两个元素进行两两比较,一共要进行n-1趟。故冒泡排序代码的算法框架是不需要改变的。如下所示:

void bubble_sort (void* base, size_t num, size_t size, 
				int(*cmp)(const void* p1, const void* p2))
{
	//趟数
	int i = 0;
	for (i = 0; i < num - 1; i++)
	{
		//每趟两两交换次数
		int j = 0;
		for (j = 0; j < num - 1 - i; j++)
		{
			if ()//比较两个元素的大小
			{
				//交换两个元素
			}
		}
	}
}

  当执行到if(……),该如何实现比较两个元素的大小?由于待比较的两个元素的类型不确定,比较的方法自然也不同。所以只能是调用函数方,自己实现比较函数,并将函数的地址作为参数传递进来。但是问题来了,由于此时指向函数起始位置指针basevoid*类型的,无法进行加减整数操作,那么调用函数的时候,待比较的两个元素的地址该如何获得?
  如果待比较的两个元素是int型,那么加减整数一次就需要跳过1个整型4个byte。但是我们在实现排序函数时,是不知道两个元素是int型的,所以不能将base指针强制类型转化为(int*)。该怎么办呢?
  解决方案:将base指针强制类型转换为(char*)类型,那么此时base指针加减1就能跳过1个byte。想要找到元素的地址,就只需要将base + (下标)*(元素的大小)即可。如下所示:

void bubble_sort(void* base, size_t num, size_t size, int cmp(const void* p1, const void* p2))
{
	//趟数
	int i = 0;
	for (i = 0; i < num - 1; i++)
	{
		//每趟两两交换次数
		int j = 0;
		for (j = 0; j < num - 1 - i; j++)
		{
			if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0)
			{
				//……
			}
		}
	}
}

  继续往下执行,需要实现交换两个元素。但问题来了,由于不确定交换的两个元素的类型,无法通过解引用操作一次性访问整个元素来进行交换。该怎么解决?
  解决方案:先将元素的地址强制类型转化为(char*),一个字节一个字节的进行交换,一共交换size元素大小次,就能完成两个元素的交换了。将交换两个元素封装成一共函数,并将两个元素的地址和元素的大小作为参数传递给函数。如下所示:

void swap(void* s1, void* s2, size_t size)
{
	int i = 0;
	for (i = 0; i < size; i++)
	{
		char tmp = *((char*)s1 + i);
		*((char*)s1 + i) = *((char*)s2 + i);
		*((char*)s2 + i) = tmp;
	}
}

void bubble_sort(void* base, size_t num, size_t size, int cmp(const void* p1, const void* p2))
{
	//趟数
	int i = 0;
	for (i = 0; i < num - 1; i++)
	{
		//每趟两两交换次数
		int j = 0;
		for (j = 0; j < num - 1 - i; j++)
		{
			if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0)
			{
				swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
			}
		}
	}
}

  下面来验证一下模拟实现的通用冒泡排序函数是否可行。

  1. 排序整型

#include<stdio.h>

void swap(void* s1, void* s2, size_t size)
{
	int i = 0;
	for (i = 0; i < size; i++)
	{
		char tmp = *((char*)s1 + i);
		*((char*)s1 + i) = *((char*)s2 + i);
		*((char*)s2 + i) = tmp;
	}
}

void bubble_sort(void* base, size_t num, size_t size, int cmp(const void* p1, const void* p2))
{
	//趟数
	int i = 0;
	for (i = 0; i < num - 1; i++)
	{
		//每趟两两交换次数
		int j = 0;
		for (j = 0; j < num - 1 - i; j++)
		{
			if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0)
			{
				swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
			}
		}
	}
}

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

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

int main()
{
	int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
	int num = sizeof(arr) / sizeof(arr[0]);
	int size = sizeof(arr[0]);
	bubble_sort(arr, num, size, cmp_int);
	print(arr, num);
	return 0;
}

在这里插入图片描述

  1. 排序结构体
#include<stdio.h>
#include<string.h>

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

void swap(void* s1, void* s2, size_t size)
{
	int i = 0;
	for (i = 0; i < size; i++)
	{
		char tmp = *((char*)s1 + i);
		*((char*)s1 + i) = *((char*)s2 + i);
		*((char*)s2 + i) = tmp;
	}
}

void bubble_sort(void* base, size_t num, size_t size, int cmp(const void* p1, const void* p2))
{
	//趟数
	int i = 0;
	for (i = 0; i < num - 1; i++)
	{
		//每趟两两交换次数
		int j = 0;
		for (j = 0; j < num - 1 - i; j++)
		{
			if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0)
			{
				swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
			}
		}
	}
}

void print(stu arr[], size_t num)
{
	int i = 0;
	for (i = 0; i < num; i++)
	{
		printf("%s %d\n", arr[i].name, arr[i].age);
	}
	printf("\n");
}

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

int main()
{
	stu arr[] = { {"zhangsan", 25},{"lisi", 38},{"wangwu", 17} };
	size_t num = sizeof(arr) / sizeof(arr[0]);
	size_t size = sizeof(arr[0]);
	bubble_sort(arr, num, size, cmp_stu_name);
	print(arr, num);
	return 0;
}

在这里插入图片描述


在这里插入图片描述
这份博客👍如果对你有帮助,给博主一个免费的点赞以示鼓励欢迎各位🔎点赞👍评论收藏⭐️,谢谢!!!
如果有什么疑问或不同的见解,欢迎评论区留言欧👀。

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

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

相关文章

828华为云征文|部署多媒体流媒体平台 Plex

828华为云征文&#xff5c;部署多媒体流媒体平台 Plex 一、Flexus云服务器X实例介绍1.1 云服务器介绍1.2 性能模式1.3 计费模式 二、Flexus云服务器X实例配置2.1 重置密码2.2 服务器连接2.3 安全组配置 三、部署 Plex3.1 Plex 介绍3.2 Docker 环境搭建3.3 Plex 部署3.4 Plex 使…

Apple M3编译OpenSSL安卓平台SO库

1.下载OpenSSL源码: https://github.com/openssl/openssl.git 2.配置NDK环境变量:vim ~/.zprofile 添加ANDROID_NDK_ROOT环境变量,iosdev改为你自己的用户名 export ANDROID_NDK_ROOT=/Users/iosdev/Library/Android/sdk/ndk/23.1.7779620 添加NDK下可执行文件路径到PATH环…

Java修仙之路,十万字吐血整理全网最完整Java学习笔记(进阶篇)

导航&#xff1a; 【Java笔记踩坑汇总】Java基础JavaWebSSMSpringBootSpringCloud瑞吉外卖/谷粒商城/学成在线设计模式面试题汇总性能调优/架构设计源码解析 推荐视频&#xff1a; 黑马程序员全套Java教程_哔哩哔哩 尚硅谷Java入门视频教程_哔哩哔哩 推荐书籍&#xff1a; 《Ja…

ubuntu2204安装kvm

ubuntu2204安装kvm 前言一、检测硬件是否支持二、安装软件三、创建/管理虚拟机1、创建存储池2、qemu创建镜像3、xml文件运行虚拟机1、范文2、xml文件创建虚机3、创建虚机 4、克隆虚机5、创建快照6、脚本创建VNC连接 四、创建集群1、安装glusterfs2、加入集群删除节点 3、 创建卷…

EasyExcel 动态表头+表头合并

EasyExcel 动态表头表头合并 ​ 最终呈现效果&#xff1a; ​ 以前对EasyExcel的使用都是一个实体类字段对应一列&#xff0c;通过以下来一一对应即可。 ExcelProperty(index 0,value "姓名" ) private String xm;​ 所以此中出现的两个问题&#xff1a; 表头合并…

【LeetCode每日一题】——LCR 168.丑数

文章目录 一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目注意】六【题目示例】七【题目提示】八【解题思路】九【时间频度】十【代码实现】十一【提交结果】 一【题目类别】 优先队列 二【题目难度】 中等 三【题目编号】 LCR 168.丑数 四【题目描述…

多输入多输出 | Matlab实现SO-BP蛇群算法优化BP神经网络多输入多输出预测

多输入多输出 | Matlab实现SO-BP蛇群算法优化BP神经网络多输入多输出预测 目录 多输入多输出 | Matlab实现SO-BP蛇群算法优化BP神经网络多输入多输出预测预测效果基本介绍程序设计往期精彩参考资料 预测效果 基本介绍 多输入多输出 | Matlab实现SO-BP蛇群算法优化BP神经网络多输…

1688竞品分析这样做,超越电商同行,流量想不爆都难!

竞品分析最大的意义就是知己知彼&#xff01;清楚自己所在的位置&#xff0c;取长补短&#xff0c;确定下一阶段打法和方向。那么该怎么做竞品分析&#xff1f; 我们利用店雷达1688工具进行实操讲解&#xff0c;分别从竞品目标、价格机制、流量结构&#xff0c;3个方面教你迅速…

uniapp 做一个查看图片的组件,图片可缩放移动

因为是手机端&#xff0c;所以需要触摸可移动&#xff0c;双指放大缩小。 首先在components里建个组件 查看图片使用 uni-popup 弹窗 要注意 transform的translate和scale属性在同一标签上不会一起生效 移动就根据触摸效果进行偏移图片 缩放就根据双指距离的变大变小进行缩…

前端练习小项目 —— 养一只电子蜘蛛

前言&#xff1a;在学习完JavaScript之后&#xff0c;我们就可以使用JavaScript来实现一下好玩的效果了&#xff0c;本篇文章讲解的是如何纯使用JavaScript来实现一个网页中的电子蜘蛛。 ✨✨✨这里是秋刀鱼不做梦的BLOG ✨✨✨想要了解更多内容可以访问我的主页秋刀鱼不做梦-C…

2.3.1 协程设计原理与汇编实现coroutine

LINUX 精通 7 day23 20240908 晚19&#xff1a;25 - 21:30 课程链接地址 2.3.1 协程设计原理与汇编实现coroutine 目的 协程不是某种语言特有的&#xff0c;lua&#xff0c;go都有 ntyco 是king老师自己写的 原语操作&#xff1a;“原语操作”通常指的是在编程或计算机科学中…

Android 12系统源码_窗口管理(八)WindowConfiguration的作用

前言 在Android系统中WindowConfiguration这个类用于管理与窗口相关的设置&#xff0c;该类存储了当前窗口的显示区域、屏幕的旋转方向、窗口模式等参数&#xff0c;应用程序通过该类提供的信息可以更好的适配不同的屏幕布局和窗口环境&#xff0c;以提高用户体验。 一、类定…

性能测试的复习2-jmeter的搭建、使用、参数化

通过网盘分享的文件&#xff1a;性能测试共享文件 链接: https://pan.baidu.com/s/1A4Nc8C5Xp6qxQ5QFtecK8g?pwds73c 提取码: s73c 1、性能测试工具 2、jmeter环境搭建 3、jmeter的基本使用 4、jmeter的参数化

strncpy函数的使用和模拟实现

目录 1.头文件 2.strncpy函数功能 2.1情况二&#xff1a; 3.strncpy函数&#xff08;模拟实现&#xff09; 方源一把抓住VS2022&#xff0c;催动春秋产的气息&#xff0c;顷刻炼化&#xff01; 1.头文件 strncpy函数的使用需要包括头文件<string.h> #include<string…

Windows系统好用软件推荐

uTools uTools官网&#xff1a;https://u.tools/download/ 功能介绍&#xff1a; 内置许多有用的插件、快速打开应用、复制图片保存等

4457E/4457F/4457G/4457K数字示波器

KEYSIGHT是德 4457E/4457F/4457G/4457K数字示波器 4457系列数字示波器共4个产品型号&#xff0c;产品带宽从1GHz到4GHz&#xff0c;采样率10GSa/s、20GSa/s&#xff0c;垂直分辨率8bit&#xff0c;存储深度2Gpts&#xff0c;最快波形捕获率120万个波形/秒&#xff0c;独创的An…

LIN帧显隐性电平和字节传输顺序理解

&#x1f345; 我是蚂蚁小兵&#xff0c;专注于车载诊断领域&#xff0c;尤其擅长于对CANoe工具的使用&#x1f345; 寻找组织 &#xff0c;答疑解惑&#xff0c;摸鱼聊天&#xff0c;博客源码&#xff0c;点击加入&#x1f449;【相亲相爱一家人】&#x1f345; 玩转CANoe&…

干耳屎硬掏不出来怎么办?质量最好的可视挖耳勺推荐

干耳朵的朋友都会有这样子的困扰&#xff0c;耳朵中的耳屎太硬挖不出来。用铁耳勺去挖会疼&#xff0c;还容易因为操作不当弄伤耳膜。而用棉签掏耳&#xff0c;只有越推越进去。所以当耳屎弄不出来是可以用专业的工具来挖取。市面上的可视挖耳勺通过内窥镜观察耳道中的情况。但…

电脑文件被删如何找回?选它,文件恢复又快又全面!

在我们的日常工作和生活中&#xff0c;文件是无比重要的存在。它可能是您精心撰写的报告&#xff0c;可能是珍贵的照片回忆&#xff0c;也可能是多年积累的工作资料。然而&#xff0c;有时一个不小心&#xff0c;文件可能就被我们删除了&#xff0c;那种焦急和无奈想必您也曾体…

解锁阿尔茨海默病(AD)靶点密码,开启靶向治疗新篇章

前 言&#xff1a; 阿尔茨海默病&#xff08;AD&#xff09;是一种严重的神经退行性疾病&#xff0c;多发于高龄人群&#xff0c;主要表现为记忆、思维、分析判断、视空间辨认、情绪等障碍。从实验室到临床应用的过程充满挑战。阿尔茨海默症新型疗法的开发主要聚焦于靶向Aβ、…