【C语言进阶篇】回调函数都学了吧!那么用冒泡排序实现qsort函数你会嘛?

news2024/9/28 11:25:27

在这里插入图片描述

🎬 鸽芷咕:个人主页

 🔥 个人专栏:《C语言初阶篇》 《C语言进阶篇》

⛺️生活的理想,就是为了理想的生活!

文章目录

  • 📋 前言
  • 💬 qsort 和 冒泡排序的区别
        • 📑 qsort 的特点
        • 📑 冒泡排序 的特点
    • 💭 如何解决只能排序整形
        • 📖(void *)指针讲解
        • 📖(void* )类型的指针该如何使用
      • ✅ 解决方法
    • 💭 如何解决只能排序整形
      • 📖 冒泡排序需要改进的地方
        • ✅ 改进方法
        • ✅ 参数讲解
    • 💭 如何解决不同类型的交换问题
        • ✅ swap交换函数的实现
  • 💬 bubble_sort实现完全体
    • 💭 bubble_sort完整代码
        • 🌈 测试排序整形数组
        • 🌈 测试排序结构体
  • 📝全篇总结

📋 前言

  🌈hello! 各位宝子们大家好啊,前面一章讲解了qsor快排函数的使用那么我们是否可以自己实现一下他呢?
  ⛳️冒泡排序我们都知道只能排序整形,但是回调函数学完了之后就可以完美解决这个问题,下面就来看看吧!
  📚本期文章收录在《C语言进阶篇》,大家有兴趣可以看看呐
  ⛺️ 欢迎铁汁们 ✔️ 点赞 👍 收藏 ⭐留言 📝!

🔥 注:VS2022 等C语言学习工具都在《学习工具专栏》, 还有各种实用调试技巧有兴趣可以去看看呐!

💬 qsort 和 冒泡排序的区别

📑 qsort 的特点

🔥 注:快排函数qsort的使用博主在《qsort的使用详解》详细讲解过哦,不会可以去看看。

qsort的特点是:

  • 可以排序任意类型的数据
  • 使用快速排序的思想 quick

📑 冒泡排序 的特点

冒泡排序 的特点:

  • 只能排序整形数据

冒泡排序 思想:

  • 俩俩相邻的元素进行比较,满足条件就交换

好了这俩种排序的思想和区别我们都明白了!冒泡排序我相信大家都不陌生,那么我们今天的任务就是使用冒泡排序的思想去模拟实现库函数qsort 函数的功能!

  • 而这需要解决冒泡排序3个缺陷
  • 一、只能排序整形
  • 二、不同类型的数据比较方法不一样
  • 三、不同类型数据如何交换方法也不一样

💭 如何解决只能排序整形

这个是冒泡排序最主要的问题,那么改如何解决呢?既然是模拟实现qsort 函数那么我们就可以借鉴一下 qsort 函数的方法!

  • qsort 函数里面直接用 通用类型指针 接收的数据
  • 通用类型指针 是不是刚好能解决冒泡排序只能接收整数的问题
    在这里插入图片描述

📖(void *)指针讲解

void我们都知道是一个空类型的意思,void 就是无类型的指针 :*

  • 无具体类型的指针,可以说他为通用类型指针
  • 但是这种类型的指针是不能够直接进行解引用操作的
  • 由于类型是空类型所以也不能进行指针运算
  • 因为既然他是个空类型那么我们 + - 是该跳过多少字节呢?

📚示例一:
在这里插入图片描述

  ⛳️这里就就可以看出一旦指针类型不同是不可以接收不同类型的地址的!

  • 而用 void* 类型的指针就不会出现这种情况

📚示例二:
在这里插入图片描述

📖(void* )类型的指针该如何使用

  ⛳️前面说了这种指针既不能直接解引用,又不能进行指针运算那么我们该怎么使用void*类型的指针呢?

  • 🌱 其实void*类型的指针在使用的时候需要强制转换一下就好了!
  • 🌱 这样这个空指针类型不就有类型了(我们强制转换的类型)
  • 🌱 那么指针的运算不也解决了?因为有类型了就可以知道
  • 🌱 加一步我们可以跳过多少字节

📑图片展示:
在这里插入图片描述

✅ 解决方法

现在我们知道了 qsort 快排函数的参数 和 通用类型指针 void* 如何使用那么解决冒泡排序只能排序整形还不简单嘛?

  • 既然是模拟实现 qsort 那么就先仿着 qsort 的参数写
  • 来实现我们的冒泡排序 bubble_sort
    在这里插入图片描述

📚 代码演示:

//模拟实现 qsort 
void bubble_sort(void* base, //第一个参数的地址
				 size_t num,//要比较元素的个数
				 size_t size, //比较元素的大小
				int (*cmp)(const void* , const void*) )
				//比较函数的地址

这里我们就把要模拟实现的函数 bubble_sort 的参数给写好了,由于我们也要排序不同类型的参数所以,肯定是需要元素类型大小

  • 从哪里排序的第一个参数地址
  • 以及要排序多少个元素
  • 和每个元素怎么进行比较

💭 如何解决只能排序整形

大家都知道冒泡排序在比较整数的时候字需要简单的进行比个大小就好了。但是我们这里需要对不同类型进行比较就不能进行以前那种简单的比较方法了!

  • 那么该怎么解决呢?这个其实也很简单 qsort
  • 库函数里面需要我们自己写一个比较函数来进行判断如何比较
  • 那么我们也可以使用这种方法,对于不同的数据由使用者来决定如何比较
  • 我们只需要调用就好了。

📖 冒泡排序需要改进的地方

在这里插入图片描述

✅ 改进方法

📚 代码演示:

这里我们可以怎么改进呢?前面说了对于不同的数据由使用者来决定如何比较!我们只需要写一个回调函数就好了!

  • 使用回调函数就可以在这里解决问题
  • 一个函数可以调用多种不同的函数

🔥 注: 回调函数的详细讲解和使用实例在这里《回调函数的实战技巧》

void bubble_sort(void* base, size_t num, size_t size, int (*cmp)(const void* , const void*) )
{
	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)
			{
				int tmp = arr[i];
				arr[i] = arr[i + 1];
				arr[i + 1] = tmp;
			}
		}
	}
}

✅ 参数讲解

cmp((char*)base+jsize, (char)base +( j +1)* size)>0
这个函数调用是如何写出来的呢? 虽然我们的比较函数是由使用者来实现的!但是我们只是可以调用函数,而函数的参数还是需要我们在 bubble_sort 里面传出去的。

  • 既然要比较就需要 第一个 第二个 俩个相邻的元素
  • void* 类型的指针又不能直接使用,我们还要排序不同类型的元素所以类型转换就不能写死
  • 把它强转为 (char*) 是最合理的,一个字节!

而我们又知道每个元素的类型大小是多少,这不就和巧妙嘛!(char*)base+j*size char* 指针是每次访问一个字节,那么乘上我们的元素类型大小就刚好可以访问不同类型的元素!

  • 假设我们参数是整形数组
  • 那么 (char*)base+j*size 就是访问4个字节(char*)base+j*4
  • 刚好是一个整形的大小。

💭 如何解决不同类型的交换问题

而冒泡排序以前的交换算法也肯定不可取了,这就需要我们自己构建一种可以交换任意类型的数据了!
在这里插入图片描述

✅ swap交换函数的实现

既然是交换那么肯定需要俩个参数,所以 (char*)base+j*size(char*)base +( j +1)* size) 这俩个参数肯定是需要的

  • 而我们传过来的参数是强转成 char* 的
  • 那么我们是不是可以这样交换
    在这里插入图片描述

一个字节一个字节的进行交换,诶是不是很神奇。这样是不是也能把俩个元素个相互交换并且内容都不发生改变。

  • 因为交换的本质还是一样,只不是以前是一步完成的!
  • 我们现在交换了4次,只是次数变多了但结果是一样的!
  • 都是把不同字节的内容给交换了!
  • 只需要知道要交换元素类型大小是多少,所以我们还需要一个类型大小 size 传过来!

📚 代码演示:

//交换函数
void swap(char* p1, char* p2,int size)
{
	int i = 0;
	for (i = 0; i < size; i++)
	{
		char tmp = *p1;
		*p1 = *p2;
		*p2 = tmp;
		 p1++;
		 p2++;
	}
}

💬 bubble_sort实现完全体

好了这下我们冒泡排序的所有缺点都解决了,折现就可以验证一下 bubble_sort 冒泡排序模拟实现的 qsort 在功能上是不是一样的!

💭 bubble_sort完整代码

📚 代码演示:

//交换函数
void swap(char* p1, char* p2,int size)
{
	int i = 0;
	for (i = 0; i < size; i++)
	{
		char tmp = *p1;
		*p1 = *p2;
		*p2 = tmp;
		 p1++;
		 p2++;
	}
}

//测试 bubble_sort 整数排序
//void qsort(void* base, size_t num, size_t size,
	//int (*compar)(const void*, const void*))
void bubble_sort(void* base, size_t num, size_t size, int (*cmp)(const void* , const void*) )
{
	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);
			}
		}
	}
}

🌈 测试排序整形数组

📚 代码演示:

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


//交换函数
void swap(char* p1, char* p2,int size)
{
	int i = 0;
	for (i = 0; i < size; i++)
	{
		char tmp = *p1;
		*p1 = *p2;
		*p2 = tmp;
		 p1++;
		 p2++;
	}
}

//测试 bubble_sort 整数排序
//void qsort(void* base, size_t num, size_t size,
	//int (*compar)(const void*, const void*))
void bubble_sort(void* base, size_t num, size_t size, int (*cmp)(const void* , const void*) )
{
	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 int_cmp(const void* p1, const void* p2)
{
	return (*(int*)p1) - (*(int*)p2);
}


test1()
{
	int arr[10] = { 1,9,5,3,4,2,10,8,7,6 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr, sz, sizeof(arr[0]), int_cmp);
	//打印函数

}


int main()
{
	test1();
	return 0;
}

🌈 测试排序结构体

📚 代码演示:

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

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

//交换函数
void swap(char* p1, char* p2,int size)
{
	int i = 0;
	for (i = 0; i < size; i++)
	{
		char tmp = *p1;
		*p1 = *p2;
		*p2 = tmp;
		 p1++;
		 p2++;
	}
}

//测试 bubble_sort 整数排序
//void qsort(void* base, size_t num, size_t size,
	//int (*compar)(const void*, const void*))
void bubble_sort(void* base, size_t num, size_t size, int (*cmp)(const void* , const void*) )
{
	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 struct_cmp(const void* p1, const void* p2)
{
	return strcmp( ((struct stu*)p1)->name,((struct stu*)p2)->name);
}




//测试排序结构体
void test2()
{
	struct stu arr[3] = { {"zhangsan",20},{"lisi",40},{"wangwu",33} };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr, sz, sizeof(arr[0]), struct_cmp);
}
int main()
{
	test2();
	return 0;
}

📝全篇总结

✅ 归纳:
好了以上就是关于模拟实现qsort 函数的全部讲解了,学会这些你就可以完美的使用回调函数!
  qsort函数和冒泡排序的区别
  只能排序整形的改进方法
  判断部分的改进
  交换函数的实现
  测试数据
☁️ 以上就全部内容了,本章是对回调函数的应用和提高大家学会了嘛!
看到这里了还不给博主扣个:
⛳️ 点赞☀️收藏 ⭐️ 关注

💛 💙 💜 ❤️ 💚💓 💗 💕 💞 💘 💖
拜托拜托这个真的很重要!
你们的点赞就是博主更新最大的动力!
有问题可以评论或者私信呢秒回哦。
在这里插入图片描述

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

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

相关文章

干货 | 常见电路板GND与外壳GND之间接一个电阻一个电容,为什么?

干货 | 常见电路板GND与外壳GND之间接一个电阻一个电容&#xff0c;为什么&#xff1f; 外壳是金属的&#xff0c;中间是一个螺丝孔&#xff0c;也就是跟大地连接起来了。这里通过一个1M的电阻跟一个0.1uF的电容并联&#xff0c;跟电路板的地连接在一起&#xff0c;这样有什么好…

Michael.W基于Foundry精读Openzeppelin第14期——SafeMath.sol

Michael.W基于Foundry精读Openzeppelin第14期——SafeMath.sol 0. 版本0.1 SafeMath.sol 1. 目标合约2. 代码精读2.1 tryAdd(uint256 a, uint256 b) && trySub(uint256 a, uint256 b) && tryMul(uint256 a, uint256 b) && tryDiv(uint256 a, uint256 b…

CSS鼠标样式(cursor)

CSS cursor 属性值 属性值示意图描述auto默认值&#xff0c;由浏览器根据当前上下文确定要显示的光标样式default 默认光标&#xff0c;不考虑上下文&#xff0c;通常是一个箭头none不显示光标initial将此属性设置为其默认值inherit从父元素基础 cursor 属性的值context-menu…

JVM理论(七)性能监控与调优

概述 性能优化的步骤 性能监控&#xff1a;就是通过以非强行或入侵方式收集或查看应用程序运行状态,包括如下问题 GC频繁CPU过载过高OOM内存泄漏死锁程序响应时间较长性能分析&#xff1a;通常在系统测试环境或者开发环境进行分析 通过查看程序日志以及GC日志,或者运用命令行工…

simulink与遗传算法结合求解TSP问题

前言&#xff1a;刚开始入门学习simulink&#xff0c;了解了基本的模块功能后想尝试从自己熟悉的领域入手&#xff0c;自己出题使用simulink搭建模型。选择的是TSP问题的遗传算法&#xff0c;考虑如何用simulink建模思想来实现一个简单TSP问题的遗传算法。 TSP问题描述 一个配…

注解和反射04--类加载

类加载 Java内存分析了解类的加载过程类的加载与ClassLoader的理解什么时候会发生类的初始化 类加载器类加载器的作用 Java内存分析 了解类的加载过程 当程序主动使用某个类是&#xff0c;如果该类害未被加载到内存中&#xff0c;啧系统会通过下面三个步骤来对该类进行初始化 …

数字信号处理中的基本运算——加法运算

1. 一位全加器 2. 二进制加法原理 两个N位二进制补码相加&#xff0c;为防止溢出时导致计算结果错误&#xff0c;可将这两个加数先进行符号位扩展&#xff0c;变为N1位二进制数&#xff0c;然后相加&#xff0c;结果亦取N1位&#xff0c;可保证运算结果正确。 根据多位加法器…

互联网医院申办|线上问诊系统|互联网医院系统功能

随着互联网的快速发展&#xff0c;各行各业都在积极探索如何将互联网与自身服务相结合&#xff0c;实现数字化转型。互联网医院建设分院内与院外建设&#xff0c;院内建设是业务流程的优化过程&#xff0c;是系统改造的过程&#xff0c;是医院精细化运营的一部分&#xff0c;也…

基于Java+SpringBoot+vue前后端分离美容院管理系统设计实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

跑步适合戴什么样的耳机、最好的跑步耳机推荐

每个人对于运动的方式都不尽相同&#xff0c;但大多数热爱运动的朋友都离不开音乐的陪伴。运动和带有节奏感的音乐能够激发我们更多的热情和动力。特别是在夏日的时候&#xff0c;我非常喜欢跑步。在酷热的天气里&#xff0c;如果没有音乐的伴随&#xff0c;跑步会变得单调乏味…

【Vue3】BEM 架构和 Sass 语法

1. BEM 架构 BEM&#xff08;Block, Element, Modifier&#xff09;是一种命名约定&#xff0c;用于在编写 CSS 和 HTML 类名时创建可维护和可重用的样式。BEM 是一种常用的 CSS 命名规范&#xff0c;它的目的是减少样式之间的耦合&#xff0c;增加样式的可读性&#xff0c;并…

多目标优化:NSGA(Ⅱ)

多目标优化的基本概念 习多目标优化的过程中&#xff0c;其中涉及相关概念如下&#xff1a; Pareto 支配关系 (Pareto Dominance)&#xff1a;支配&#xff1a;对于多个目标值&#xff0c;随机自变量x1、x2&#xff0c;对于任意一个目标函数都存在f(x1)<f(x2)&#xff0c;则…

Vue 3:玩一下web前端技术(三)

前言 本章内容为VUE工作过程与相关使用讨论。 上一篇文章地址&#xff1a; Vue 3&#xff1a;玩一下web前端技术&#xff08;二&#xff09;_Lion King的博客-CSDN博客 下一篇文章地址&#xff1a; Vue 3&#xff1a;玩一下web前端技术&#xff08;四&#xff09;_Lion Ki…

视频监控综合管理平台EasyCVR向上级联时,上级一直回复401是什么原因?

视频监控管理EasyCVR视频融合平台基于云边端一体化架构&#xff0c;可支持多协议、多类型设备接入&#xff0c;具体包括&#xff1a;NVR、IPC、视频编码器、无人机、车载设备、智能手持终端、移动执法仪等。平台具有强大的数据接入、处理及分发能力&#xff0c;可在复杂的网络环…

SSM 书籍借阅管理系统【纯干货分享,免费领源码04770】

摘 要 随着科学技术的告诉发展&#xff0c;我们已经步入数字化、网络化的时代。图书馆是学校的文献信息中心&#xff0c;是为全校教学和科学研究服务的学术性机构&#xff0c;是学校信息化的重要基地。图书馆的工作是学校和科学研究工作的重要组成部分&#xff0c;是全校师生学…

向量距离度量中的几种计算公式

距离度量 衡量两条向量之间的距离&#xff0c;可以将某一张图片通过特征提取来转换为一个特征向量。衡量两张图片的相似度就可以通过衡量这两张图片对应的两个特征向量之间的距离来判断了。 1.欧式距离 欧式距离可以简单理解为两点之间的直线距离。对于两个n维空间点 a ( x…

实景三维在智慧矿山中的应用

项目背景 智慧矿山是以矿山数字化、信息化为前提和基础&#xff0c;对矿山生产、职业健康与安全、技术支持与后勤保障等进行主动感知、自动分析、快速处理&#xff0c;建设智慧矿山&#xff0c;最终实现安全矿山、无人矿山、高效矿山、清洁矿山的建设。 智慧矿山的可视化管理…

解决新版 Idea 中 SpringBoot 热部署不生效

标题 依赖中添加 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <opt…

组件开发系列--Apache Commons Chain

一、前言 Commons-chain是apache commons中的一个子项目,主要被使用在"责任链"的场景中,struts中action的调用过程,就是使用了"chain"框架做支撑.如果你的项目中,也有基于此种场景的需求,可以考虑使用它. 在责任链模式里&#xff0c;很多对象由每一个对象对…

纯nginx制作文件上传下载服务器

什么是webdav webdav 是一组超文本传输协议的技术集合&#xff0c;有利于用户键协同编辑和管理存储在万维网服务器文档。同时来说就是&#xff0c;webdav可以让用户直接存储&#xff0c;下载&#xff0c;编辑文件&#xff0c;操作文件需要进行用户认证 基于nginx快速搭建webdav…