【冒泡排序】模仿qsort的功能实现一个通用的冒泡排序

news2024/11/28 6:33:44

文章目录

  • 前言
  • 曾经学的冒泡排序存在着一些局限性
  • 首先第一步:写一个main()函数,分装一个test1函数
      • ==test1函数 用来描写类型的性状==
  • 在test1创建了bubble_int 函数,下一步就是实现它,分两步走
    • 步骤一:写函数参数
    • 步骤二:写具体代码实现比较
  • 所以我们先完善一下cmp_int函数。
  • 接下来继续完善if()里面的内容,
    • 1.调用cmp函数
    • 2.cmp函数里传两个元素的地址
    • 3.cmp_int的参数用指针接收两个元素的地址
    • 4.cmp_int对两个元素解引用,作差
    • 5.cmp_int返回结果给cmp
    • 6.cmp判断结果是否>0
    • 7.>0,进入循环
    • 8.循环里调用一个交换函数Swap,传两个元素的地址,和大小
    • 9.Swap函数接收到buf1和buf2的地址,以及大小
    • 10.分装打印函数,把结果打印出来
  • 总结
    • 完整的代码如下:


前言

上一章讲了qsort如何排序各种类型数据,本章继续学习如何模仿qsort的功能实现一个通用的冒泡排序


这个冒泡排序要具备:
1.使用冒泡排序的思想
2.适应于任意类型数据的排序

void* 是实现一个通用的冒泡排序最核心的部位
因为void* 类型的指针可以接收任意类型的地址

                        假设排序整型

曾经学的冒泡排序存在着一些局限性

在这里插入图片描述
解决方法是:
在这里插入图片描述
对于问题三,后面会讲到

首先第一步:写一个main()函数,分装一个test1函数

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

test1将会模仿qsort的功能:
在这里插入图片描述
四个函数的意思是:
void qsort(
void* base,//指向需要排序的数组的第一个元素
size_t num,//排序的元素个数
size_t size,//一个元素的大小,单位是字节
int(*cmp)(const void*, const void*)
);//函数指针类型-这个函数指针指向的函数,能够比较base指向数组中的两个元素

test1函数 用来描写类型的性状

test1函数里的内容具有随变性,不是固定的,但是有一个固定的框架
这里面就写你要对什么数据类型进行冒泡排序。包含四个点:

①写清楚是什么数组,有什么元素。
②一个元素的大小。
③写好等会要调用的bubble_sor函数,包括它的四个参数。
④最后分装一个打印函数,打印结果的时候要调用它。

//代码如下:
void test1()
{
	int arr[] = { 2,1,4,6,5,3,8,0,9,7 };//整型数组,有10个数字
	int sz = sizeof(arr) / sizeof(arr[0]);//一个元素大小
	bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);//模仿qsort函数的参数
	print(arr, sz);//打印函数
}

在test1创建了bubble_int 函数,下一步就是实现它,分两步走

步骤一:写函数参数

bubble_int函数的参数,如何才能接收任意类型数据呢?
图二解决一详细说了解决方法:模仿qsort函数参数

另外在bubble_int函数里,大部分代码都是固定的,可以说是一个模板,不能随意改动,而每种类型的比较方法都是不同的,所以比较的方法就不能写在bubble_int函数里。
需要另外写一个交换函数。在bubble_int函数里调用以下就可以了。
这就是为什么qsort函数的第四个参数是cmp_int函数了。所以bubble_int的第四个参数是cmp_int函数指针。

bubble_int参数总结概括为 传某个类型数组的起始位置+数组个数+一个元素大小+比较方法的函数指针
所以bubble_int的参数如下:


void bubble_sort(void* base, int num, int size, int(*cmp)(const void*, const void*))
{
	
}

步骤二:写具体代码实现比较

bubble_int里面的内容简单概括为:循环对比两个数
上图指出:不管是整形数据,浮点型数组,还是结构体数据,它们的趟数是不会变的,一趟内部比较的对数也不会变。
所以这个趟数循环的代码也是固定的。

代码如下:

void bubble_sort(void* base, int num, int 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()两个元素比较
		}
	}
}

图二说到,在if语句里的问题是:不能简单的让两个数用>来比较。
解决方法是要调用cmp_int函数指针,

所以我们先完善一下cmp_int函数。

cmp_int的两个参数是const修饰的void*指针,两个元素分别命名为p1,p2
比较方法是:两个数作差,就是p1-p2
因为已经知道是整型数组,所以要把p1,p2强制类型转换成整型,再解引用找到p1和p2的值。
注意:记得写return,因为要把作差结果返回给cmp,cmp_int前面也要用int修饰,因为有返回值

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

接下来继续完善if()里面的内容,

后面的步骤如下:

1.调用cmp函数

2.cmp函数里传两个元素的地址

因为数组类型不同,导致元素个数不同,每个元素的大小也不同,有的是一个字节,有的是四个字节,所以两个元素的地址是不一样的。
要实现通用的冒泡排序,要让cmp函数拿到不同类型的元素,就要传它们的地址过去。
所以有一个通用的地址计算公式,能找到任意数组类型的两个元素的地址。
第一个元素地址(char*)base+j*size,
第二个元素地址(char *)base+(j+1) * size

j表示数组下标,size表示字节大小,(char * )base表示是第一个元素地址,单位是一个字节
解释:
在这里插入图片描述
为什么是char类型的指针,因为一个一个字节找更准确
如下图所示:
在这里插入图片描述
当cmp接收地址,并把作差结果返回来时,如果结果小于0,就不用两数交换,如果结果大于0才需要交换位置,所以大于0才能进入if语句。

void bubble_sort(void* base, int num, int 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)
			{
				
			}
		}
	}
}

3.cmp_int的参数用指针接收两个元素的地址

4.cmp_int对两个元素解引用,作差

5.cmp_int返回结果给cmp

6.cmp判断结果是否>0

7.>0,进入循环

8.循环里调用一个交换函数Swap,传两个元素的地址,和大小

调用Swap传参时要把两个元素的地址传过去,还要传两个元素的大小siez,因为不传size,Swap函数不知道要交换多少个字节。

void bubble_sort(void* base, int num, int 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(cmp((char*)base + j * size, (char*)base + (j + 1) * size),size);
			}
		}
	}
}

9.Swap函数接收到buf1和buf2的地址,以及大小

buf1作为地址要用指针接收,而且是char*指针(第2点讲到原因)
一个一个字节交换的
比如要交换5和4,5的地址是05 00 00 00,4的地址是04 00 00 00,
Swap接收到地址和大小,便开始一个字节一个字节交换。
在这里插入图片描述
代码如下

void Swap(char* buf1,char* buf2,int size)
{
	char tmp = 0;
	int i = 0;
	for (i = 0; i < size; i++)
	{
		tmp = *buf1;//交换元素,不是交换地址
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;//地址往后找一个字节
		buf2++;
	}
}

10.分装打印函数,把结果打印出来

记得函数参数用指针接收,sz本身是地址就不用指针接收

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

总结

完整的代码如下:

#include<stdio.h>

void Swap(char* buf1,char* buf2,int size)
{
	char tmp = 0;
	int i = 0;
	for (i = 0; i < size; i++)
	{
		tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}

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

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

void bubble_sort(void* base, int num, int 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);
			}
		}
	}
}

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

以上就是模仿qsort的功能实现一个通用的冒泡排序的方法,希望对您有帮助,感谢关注感谢点赞感谢收藏!

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

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

相关文章

Matlab论文插图绘制模板第107期—标签散点图

在之前的文章中&#xff0c;分享了Matlab散点图绘制模板&#xff1a; 进一步&#xff0c;再来分享一种特殊的散点图&#xff1a;标签散点图。 先来看一下成品效果&#xff1a; 特别提示&#xff1a;本期内容『数据代码』已上传资源群中&#xff0c;加群的朋友请自行下载。有需…

内网穿透远程查看内网监控摄像头

内网穿透远程查看内网监控摄像头 在现代社会中&#xff0c;大家总是奔波于家和公司之间。大部分时间用于工作中&#xff0c;也就很难及时知晓家中的动态情况&#xff0c;对于家中有老人、小孩或宠物的&#xff08;甚至对居住环境安全不放心的&#xff09;&#xff0c;这已然是…

ubuntu下tmux安装

目录 0. 前言1. Tmux介绍2. 安装3. 验证安装 0. 前言 本节安装tmux终端复用工具&#xff0c;在Ubuntu中运行一些服务或脚本的时候往往不能退出终端&#xff0c;需要一直挂着。在有图形界面的linux中你还可以新开一个终端去做别的事&#xff0c;但是在无界面linux中&#xff0c…

re学习(22)伪造CTF(谜之操作)

思维导图&#xff1a;找flag关键之处 1.字符串 &#xff08;flag&#xff0c; sorry&#xff09; 2.导入函数&#xff1a;&#xff08;Import _scanf &#xff09; 其他函数&#xff08;敏感函数&#xff09; createfileA:将flag放在一个文件中 Createprocess&am…

基于HCL的​​​​​​​网络规划与部署综合实训报告

0、前言 本次实验是对之前有关网络规划与综合部署的综合实验&#xff0c;适合入门的同学们进行学习&#xff0c;该实验选择了使用华三模拟器进行&#xff0c;希望能够帮助大家了解相关的指令。 一、实训目的及意义 ① 掌握网络规划和设计的基本流程 从需求分析开始做起&#x…

4-2 3D images: Volumetric data Representing tabular data

本文所用到的资料下载地址 By stacking individual 2D slices into a 3D tensor, we can build volumetric data representing the 3D anatomy of a subject. We just have an extra dimension, depth, after the channel dimension, leading to a 5D tensor of shape N C D…

【MySQL进阶(三)】 InnoDB体系架构之内存池(buffer pool)

InnoDB体系架构之内存池 一、InnoDB 体系结构二、缓冲池 buffer pool内部结构free 链&#xff08;管理空闲缓冲页&#xff09;怎么知道数据页是否被缓存&#xff1f; flush 链表&#xff08;管理脏页&#xff09;1. 脏页2. 链表结构3. 刷盘时机 LRU 链表&#xff08;控制数据热…

blender 纹理材质

添加材质纹理需要哪五个节点&#xff1f; 映射节点&#xff1a;调整纹理的位置、大小、缩放&#xff1b; 纹理坐标&#xff1a;怎么映射&#xff0c;以什么方式去映射这张图&#xff0c;换句话说就是如何将 2D 的图片映射到 3D 的图像上&#xff1b;纹理坐标就是以什么坐标方式…

【学会动态规划】下降路径最小和(8)

目录 动态规划怎么学&#xff1f; 1. 题目解析 2. 算法原理 1. 状态表示 2. 状态转移方程 3. 初始化 4. 填表顺序 5. 返回值 3. 代码编写 写在最后&#xff1a; 动态规划怎么学&#xff1f; 学习一个算法没有捷径&#xff0c;更何况是学习动态规划&#xff0c; 跟我…

Rust学习01:D-day

以前自学过Python&#xff0c;开发了一些小程序&#xff0c;用于工作中提升效率。 Python的确好学易用&#xff0c;但用来做一个真正意义上的产品&#xff0c;哪怕是比较简单的产品&#xff0c;差点意思&#xff0c;特别是在移动端开发领域。 Rust看了两本书&#xff0c;准备动…

剑指offer61.扑克牌中的顺子

我的想法非常简单&#xff0c;就是先给数组排序&#xff0c;然后统计里面有几个0&#xff0c;然后遍历数组&#xff0c;如果是0或者比后面一个数小1就直接进入下一次循环&#xff0c;如果比后面一个数小2&#xff0c;就用掉一个0&#xff0c;0的数量减1&#xff0c;如果比后面的…

leetcode面试题 判断字符是否唯一

⭐️ 题目描述 &#x1f31f; leetcode链接&#xff1a;判断字符是否唯一 思路&#xff1a; a - z 的 ASCII 区间在 [97 , 122] 当中的每个减去 97 或者 a 都会变成 0 - 25&#xff0c;所以只需要一个数组&#xff0c;用当前元素减去 97 97 97 的下标来记录当前字母出现的次数…

微软、OpenAI用上“数据永动机” 合成数据是晨曦还是暮光?

微软、OpenAI、Cohere等公司已经开始测试使用合成数据来训练AI模型。Cohere首席执行官Aiden Gomez表示&#xff0c;合成数据可以适用于很多训练场景&#xff0c;只是目前尚未全面推广。 已有的&#xff08;通用&#xff09;数据资源似乎接近效能极限&#xff0c;开发人员认为&a…

volatile轻量级锁

一、背景 我们在写项目的时候&#xff0c;有时会使用多线程。为了保证一部分线程之间的通信&#xff0c;所以需要线程中的一些变量具有可见性。 说到线程可见性&#xff0c;对于Java而言&#xff0c;有两种方法实现&#xff1a;volatile和synchronized。 需要注意的是&#…

Python中TensorFlow的长短期记忆神经网络(LSTM)、指数移动平均法预测股票市场和可视化...

原文链接&#xff1a;http://tecdat.cn/?p23689 本文探索Python中的长短期记忆&#xff08;LSTM&#xff09;网络&#xff0c;以及如何使用它们来进行股市预测&#xff08;点击文末“阅读原文”获取完整代码数据&#xff09;。 相关视频 在本文中&#xff0c;你将看到如何使用…

BART模型和 Electra模型对比

总结 Electra模型在使用较少的计算资源的情况下能够达到跟大语言模型相近的效果。但BART模型对于传统的BERT中加入了不同种制造noise的方式&#xff0c;是BERT和GPT的结合体。Electra模型主要是Generator模型和Discriminator模型的结合体。 未知参数设置&#xff0c;两个模型…

【Spring Boot】事务的隔离级别与事务的传播特性详解:如何在 Spring 中使用事务?不同隔离级别的区别?

文章目录 1 事务1.1 事务简介与 mysql 中的事务使用1.2 Spring 编程式事务&#xff08;手动操作&#xff09;1.3 Spring 声明式事务&#xff08;自动操作&#xff09;1.4 Transactional 的工作原理 2 事务的隔离级别2.1 事务的四大特性及事务的隔离级别回顾2.2 Spring 事务的隔…

【Unity2D】相机移动以及设置相机边界

添加相机 添加相机时&#xff0c;首先需要在unity中添加 Cinemachine 包 第一次使用这个包时&#xff0c;需要在Package Manager中搜索并安装 安装Camera Mechine包后&#xff0c;添加2D Camera 设置跟随对象为Ruby &#xff08;从Hierarchy中将Ruby拖动到Follow中&#xff0…

非线性质量弹簧阻尼器的神经网络仿真研究(Matlab代码Simulink仿真实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f308;4 Matlab代码、Simulink仿真实现 &#x1f4a5;1 概述 非线性质量弹簧阻尼器&#xff08;Nonlinear Mass-Spring-Damper&#xff0c;NMSD&#xff09;是一种常见的振动控制装置&#…

VS2017找不到QT头文件

一、我的电脑右键属性 - 》“高级系统设置” -》“环境变量” 增加环境变量Qt_INCLUDEPATH_ 值为QT的头文件目录 二、重启VS 发现波纹线不见了&#xff0c;证明设置环境变量后VS能识别到QT头文件了 原理是&#xff1a;vs导入qt项目附加包含目录继承值有Qt_INCLUDEPATH_