【落羽的落羽 C语言篇】指针·之其五

在这里插入图片描述

文章目录

  • 一、冒泡排序
  • 二、qsort排序
    • 1. qsort使用指南
    • 2.回调函数
    • 3. qsort函数的模拟实现

一、冒泡排序

冒泡排序的核心思想就是:两两相邻的元素进行比较和交换
现在,我们想编写一个函数,使它能够运用冒泡排序的原理,由小到大排好一个乱序的整形数组。例如:假如输入5、2、1、10、9、7、3、4、8、6,能输出1、2、3、4、5、6、7、8、9、10。如果画图分许这个排序过程的每一步:
在这里插入图片描述看,经过第一轮的两两比较,较大的数排到右边,这样最大的10就来到了最右边。
在这里插入图片描述而经过第二次排序,第二大的9就能来到倒数第二个位置。

像这样继续排序,这个数组的大小是10,经过9次(10-1次)排序,就能完成1到10的排序
知道了原理,在代码层面编写这样的函数就非常简单了:

void bubble_Sort(int* p ,int sz)
{
for(int turn=1 ; turn<=sz-1 ; turn++)//要排n个数字,就需要进行n-1轮排序for(int i=0 ; i<sz-1 ; i++)if(p[i] > p[i+1]){int tmp = p[i];p[i] = p[i+1];p[i+1] = tmp;}
}

当然,为了节省运行时间,我们还可以进行一点优化:假如这个数组在第一趟排序后就已经有序了,它就可以直接停止而没必要再排序好多次。

void bubble_Sort(int* p ,int sz)
{
for(int turn=1 ; turn<=sz-1 ; turn++)
{int flag = 1;//假设这一次已经有序了for(int i=0 ; i<sz-1 ; i++){if(p[i] > p[i+1]){flag = 0;//发生了交换说明这一次还不是有序int tmp = p[i];p[i] = p[i+1];p[i+1] = tmp;}}if(flag==1)break;//这一次没有发生交换,说明已经有序了,可以退出函数了
}
}

这样,整个程序就是:

#include<stdio.h>
void bubble_Sort(int* p ,int sz)
{
for(int turn=1 ; turn<=sz-1 ; turn++)
{int flag = 1;for(int i=0 ; i<sz-1 ; i++){if(p[i] > p[i+1]){flag = 0;int tmp = p[i];p[i] = p[i+1];p[i+1] = tmp;}}if(flag==1)break;
}
}int main()
{
int arr[10]={0};
for(int i=0 ; i<10 ; i++)scanf("%d",&arr[i]);
bubble_Sort(arr, sizeof(arr)/sizeof(arr[0]));
for(int i=0 ; i<10 ; i++)printf("%d ",arr[i]);
return 0;
}

在这里插入图片描述
结果也是非常的成功~
在这里插入图片描述

二、qsort排序

1. qsort使用指南

冒泡排序只能用来排序整型数字,而且写起来太麻烦了。如果我们有了一组数据,想要直接快速按某种方式排序,该怎么办呢?
乂~C语言提供了一个库函数qsort,可以按照你想要的方式排序各种类型的数据,使用它需要包含头文件stdlib.h
在链接https://legacy.cplusplus.com/reference/clibrary/中,我们可以查到:
在这里插入图片描述

借助翻译:
在这里插入图片描述不理解也没关系,简言之,qsort函数的语法形式是:

void qsort(void* base, size_t num, size_t size, int(*compar)(const void*,const void*));

其中:

  • base指针指向的是待排序数据中的第一个元素
  • num是待排序数据的个数
  • size是待排序数据中的每一个元素的字节大小
  • compar是一个函数指针,这个函数用来比较base指向的数据中任意两个元素的大小
    注意:compar函数需要使用者自己提供,也就是要自己提供比较数据的方式。这个函数有两个参数(也就是要比较的两个元素),需要返回一个整型数。

不要着急,我们先来个简单的栗子:用qsort函数排序一个整型乱序数组:

#include<stdlib.h>
#include<stdio.h>
int compar_int(const void* p1, const void* p2)
{
return ( *(int*)p1 - *(int*)p2 );
//p1和p2一开始是void*指针,但是实际上他们指向的都是整型数据,所以要(int*)强制类型转换
}int main()
{
int arr[] = {5,2,1,10,9,7,3,4,8,6};//待排序数组
int sz = sizeof(arr)/sizeof(arr[0]);
qsort(arr, sz, sizeof(int), compar_int);
//arr:第一个元素的地址
//sz:元素个数
//sizeof(int):每个元素的字节大小
//compar_int:用来比较的函数的地址
for(int i=0 ; i<sz ; i++)printf("%d ",arr[i]);
return 0;
}

在这里插入图片描述

很完美的结果。
但我们不禁思考,这个qsort函数是怎么依靠compar函数实现交换和排序的呢?
在这里插入图片描述其实,qsort函数每次比较compar函数的两个参数,这两个指针参数指向的是任意的元素,依靠compar函数的返回值判断是否要交换:

  • 返回值为负,就把p1指向的元素放在p2指向的元素后面;
  • 返回值为0,不交换;
  • 返回值为正,就把p1指向的元素放在p2指向的元素前面。

(这里“前”指的是数组下标较大的位置,“后”是下标较小的位置)

所以,上面的程序能实现由小到大的排序,而如果你想由大到小排,只需要把compar_int里的return ( *(int*)p1 - *(int*)p2 );改为return ( *(int*)p2 - *(int*)p1 );就可以了:

在这里插入图片描述
不光是整型,如果我们想把一个字符数组按照ASCII的大小排序,也可以使用qsort函数:

#include<stdlib.h>
#include<stdio.h>
int compar_char(const void* p1, const void* p2)
{
return ( *(char*)p1 - *(char*)p2 );
//p1和p2一开始是void*指针,但是实际上他们指向的都是字符型数据,所以要(char*)强制类型转换
}int main()
{
char arr[] = {'b','t','m','a','a','x'};//待排序数组
int sz = sizeof(arr)/sizeof(arr[0]);
qsort(arr, sz, sizeof(char), compar_char);
for(int i=0 ; i<sz ; i++)printf("%c ",arr[i]);
return 0;
}

在这里插入图片描述在这里插入图片描述

2.回调函数

回调函数的概念很简单,它就是一个通过函数指针调用的函数。
如果你把函数的地址作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,被调用的函数就是回调函数了。回调函数不是由该函数的实现方直接调用的,而是在特定的条件下由另一方调用的,用于对该条件进行响应。

3. qsort函数的模拟实现

首先我要说的是,后期我们还会学习到很多的函数,他们都有不同的功能,但我们不仅要学会使用他们,还应该学会模拟实现这些函数。所谓模拟实现,是创造一个自己的函数,传递原函数相同的参数,也要能达到原函数的效果。今天,我们也应该学会模拟实现qsort函数。定义一个函数void Mine_qsort(void* base, size_t num, size_t size, int(*compar)(const void*,const void*));这就是我模拟实现的qsort函数。
而具体交换思路呢,可以分为比较、交换两步,运用了回调函数的概念和冒泡排序的思想。

#include<stdio.h>
int compar_int(const void* p1, const void* p2)
{
return (*(int*)p1 - *(int*)p2);
}void swap(const void* p1, const void* p2)
{
int tmp = *((int*)p1);
*((int*)p1) = *((int*)p2);
*((int*)p2) = tmp;
}void Mine_qsort(void* base, size_t num, size_t size, int(*compar)(const void*,const void*))
{
for(int turn=1 ; turn<=num-1 ; turn++)for(int i=0 ; i<num-1 ; i++)if(compar( (int*)base+i, (int*)base+i+1 ) > 0)swap( (int*)base+i, (int*)base+i+1 );
}int main()
{
int arr[]={2,6,4,10,5,3,1,8,7,9};
Mine_qsort(arr, sizeof(arr)/sizeof(arr[0]), sizeof(int), compar_int);
for(int i=0 ; i<10 ; i++)printf("%d ",arr[i]);
return 0;
}

这样,我们就模拟实现了qsort函数
在这里插入图片描述
然鹅,我的代码只能排序整型数组,如果要排序其他类型数据,也可以采用另外一种通法:一个字节一个字节地交换,这样能突破不同数据类型大小不同的限制。感兴趣的各位可以自行研究~

欲知后事如何,且听下回分解~
在这里插入图片描述

本篇完,感谢阅读

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

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

相关文章

前端热门面试题目[一](HTML、CSS、Javascript、Node、Vue、React)

如何设计一个前端页面&#xff0c;实现PC端访问展示Web应用&#xff0c;移动端访问展示H5应用&#xff1f; 为了实现这一功能&#xff0c;通常需要使用响应式设计或者服务器端检测用户设备并返回相应的页面。以下是一些实现方法&#xff1a; 响应式设计&#xff1a;通过CSS媒…

【知识科普】简单讲讲Socket通讯协议

文章目录 概述一、Socket协议的基本概念二、Socket协议的类型三、Socket协议的工作原理四、Socket协议的特点五、Socket协议的应用场景 报文格式一、Socket协议报文结构二、关键字段详解三、报文示例四、注意事项 Java实现socket编程服务器端代码客户端代码运行步骤 概述 Sock…

Ajax基础总结(思维导图+二维表)

一些话 刚开始学习Ajax的时候&#xff0c;感觉很模糊&#xff0c;但是好像学什么都是这样的&#xff0c;很正常&#xff0c;但是当你学习的时候要持续性敲代码&#xff0c;边敲代码其实就可以理解很多了。然后在最后的总结&#xff0c;其实做二维表之后&#xff0c;就可以区分…

具有多个表盘、心率传感器、指南针和游戏的 DIY 智能手表

在此&#xff0c;我们将使用所学到的知识&#xff0c;结合使用硬件和软件组件从头开始创建自己的智能手表。在项目的这一部分&#xff0c;您将被指导完成组装硬件组件、设置软件以及配置智能手表的设置和功能的过程。到本项目结束时&#xff0c;您将拥有一款功能齐全的智能手表…

算法魅力之牛叉的前缀和

1.什么是前缀和 前缀和算法&#xff08;Prefix Sum Algorithm&#xff09; 是一种常用的算法技巧&#xff0c;用于快速计算数组的某些子数组的和。它通过提前计算出数组中元素的累加和&#xff0c;来加速后续的区间和查询&#xff0c;特别适用于需要频繁查询子数组和的场景。 …

Java JVM(内存结构,垃圾回收,类加载,内存模型)

一、JVM 主要功能 1. 什么是 jvm&#xff1f; JVM&#xff08;Java Virtual Machine)&#xff1a;负责运行 Java 程序的核心组件。它将 Java 字节码&#xff08;.class 文件&#xff09;解释或编译为机器代码&#xff0c;并提供内存管理、垃圾回收和线程管理等功能。 JRE (J…

机器学习基础之集成学习

集成学习&#xff08;Ensemble Learning&#xff09;是一种强大的机器学习方法&#xff0c;它通过结合多个模型的预测结果来提高整体的学习效果。集成学习方法在许多实际应用中表现出了优秀的性能&#xff0c;尤其在处理复杂问题时&#xff0c;它常常能够比单一模型取得更好的结…

33 基于单片机的智能窗帘控制系统

目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 基于51单片机&#xff0c;采用DHT11温湿度传感器检测温湿度&#xff0c;滑动变阻器连接ADC0832数模转换器转换模拟,光敏传感器&#xff0c;采用GP2D12红外传感器&#xff0c;通过LCD1602显示屏显示…

使用docker-compese部署SFTPGo详解

官网&#xff1a;SFTP & FTP as a Managed Service (SaaS) and On-premise 一、SFTPGo简介 SFTPGo 是一款功能强大的文件传输服务器软件。它支持多种协议&#xff08;SFTP、SCP、FTP/S、WebDAV、HTTP/S&#xff09;和多个存储后端。 借助 SFTPGo&#xff0c;您可以利用本地…

各大浏览器(如Chrome、Firefox、Edge、Safari)的对比

浏览器如Chrome、Firefox、Edge等在功能、性能、隐私保护等方面各有特点。以下是对这些浏览器的详细对比&#xff0c;帮助你选择合适的浏览器。 1. Google Chrome 市场份额&#xff1a;Chrome是目前市场上最流行的浏览器&#xff0c;约占全球浏览器市场的65%以上。 性能&#…

我与Linux的爱恋:消息队列

​ ​ &#x1f525;个人主页&#xff1a;guoguoqiang. &#x1f525;专栏&#xff1a;Linux的学习 文章目录 消息队列的引入以及基本概念**​消息队列的基本概念** 消息队列与命名管道和共享内存的不同消息队列的原理消息队列工作流程 System V 消息队列的主要函数msggetms…

黑马2024AI+JavaWeb开发入门Day06-JDBC-Mybatis飞书作业

视频地址&#xff1a;哔哩哔哩 讲义作业飞书地址&#xff1a;day06作业 基础性作业&#xff0c;加油&#xff01; 1、SQL语句的编写 -- 1. 查询所有的性别为男(gender 为 1)的 讲师 (job 为 2) , 并根据入职时间, 对员工进行升序排序 select * from emp where gender 1 an…

【java-Neo4j 5进阶篇】- 1.批量新增数据

系列文章目录 之前的系列文章: 一、概述篇:https://blog.csdn.net/qq_40570699/article/details/143024984 二、入门篇:https://blog.csdn.net/qq_40570699/article/details/143905723 三、进阶篇: 1.批量导入数据 文章目录 系列文章目录需求场景一、解决思路二、代码1.将属性…

tp6 合成两个pdf文件(附加pdf或者替换pdf)

最近在做项目有个需求&#xff0c;项目中需要根据设置的html合同模板自动生成PDF合同供客户下载签署&#xff0c;并根据回传的已签署合同尾页来替换原来未签署合同的尾页&#xff0c;合成新的已签署合同文本。 读取两个PDF文件并合成的 具体代码记录如下&#xff1a; use set…

LWIP和FATFS 实现 FTP 服务端

目录 一、前言 二、LWIP 和 FTP 简介 1.LWIP 2.FTP 三、实现 FTP 服务端的主要步骤 1.初始化 LWIP 2.创建 FTP 服务器任务 3.处理客户端连接 4.实现 FTP 命令处理 5.文件系统操作 6.错误处理和日志记录 四、示例代码 1.创建FTP任务 2. FTP任务代码 3.处理交互数据…

3D Bounce Ball Game 有什么技巧吗?

关于3D Bounce Ball Game&#xff08;3D弹球游戏&#xff09;的开发&#xff0c;以下是一些具体的技巧和实践建议&#xff1a; 1. 物理引擎的使用&#xff1a; 在Unity中&#xff0c;使用Rigidbody组件来为游戏对象添加物理属性&#xff0c;这样可以让物体受到重力影响并发…

C++通透讲解设计模式:单一职责原则

C通透讲解设计模式&#xff1a;单一职责原则 理解单一职责原则 这里我总结了一个规律&#xff0c;可以用一句话来表示&#xff1a; 永远只做自己分内的事情 注意&#xff0c;这个“分内的事情”是逻辑上的&#xff0c;什么意思呢&#xff1f;举几个例子。 例子 例子1 我是…

Java基础访问修饰符全解析

一、Java 访问修饰符概述 Java 中的访问修饰符用于控制类、方法、变量和构造函数的可见性和访问权限&#xff0c;主要有四种&#xff1a;public、protected、default&#xff08;无修饰符&#xff09;和 private。 Java 的访问修饰符在编程中起着至关重要的作用&#xff0c;它…

llvm源码编译

0x00 获取llvm源码 获取llvm项目源码&#xff1a;git clone https://github.com/llvm/llvm-project.git 但是&#xff0c;该项目较大&#xff0c;且直接从github下载源码可能会超时失败。可利用gitee的镜像项目进行clone&#xff1a;git clone --depth 1 https://gitee.com/m…

SpringBoot源码-Spring Boot启动时控制台为何会打印logo以及自定义banner.txt文件控制台打印

1.当我们启动一个SpringBoot项目的时候&#xff0c;入口程序就是main方法&#xff0c;而在main方法中就执行了一个run方法。 SpringBootApplication public class StartApp {public static void main(String[] args) {// testSpringApplication.run(StartApp.class);} }publi…