【C语言】qsort的秘密

一,本文目标 

qsort函数可以对任意类型数据甚至是结构体内部的数据按照你想要的规则排序,它的功能很强大,可是为什么呢?

我将通过模拟实现qsort函数来让你对这整个过程有一个清晰的深刻的理解。


二,qsort函数原型

void qsort (void* base,//要排序的对象的第一个元素的首地址size_t num, //对象的个数size_t size,//每一个对象的大小 Size in bytesint (*compar)(const void*,const void*));//Pointer to a function that compares two elements.(并且这个函数要自己写)

        qsort函数底层通过快速排序进行,但是这并不是我们感兴趣的点,我想要知道qsort为什么可以对任意类型数据进行排序,与它用什么排序算法排序没有关系,所以,我们用相对简单的冒泡排序来代替快速排序的算法,把冒泡排序赋予可以对任意类型数据进行排序的强大功能。


三,冒泡排序——大数沉底,小数上浮

 对于冒泡排序,其基本思想是大数沉底,小数上浮;

这里直接给出源码:

void Bubble_sort(int arr[], int size)
{int j,i;for (i = 0; i < size-1;i++)//排序趟数等于元素个数-1{int f = 0;for (j = 0; j < size - 1 - i; j++)//每一趟都有一个元素复位,需要排序的次数每次-1{if ( arr[j] > arr[j+1] ){int tem = arr[j];arr[j] = arr[j+1];arr[j+1] = tem;f = 1;}}if (f == 0)	{break;}}

3.1相对于qsort,冒泡排序的局限性

        只能排序整形数据 

四,两个问题:

4.1不定类型比较问题

        1.对于if内部的判断,虽然字符型可以用 >,<,= 比较,但是对于字符串类型,无法通过常规方法比较;

4.2不定类型交换问题

        2.对于交换部分,由于不同元素的类型不同,占用的内存空间也不同,如何判断传入数据的类型,并实现两个数据的交换呢?


五,改造冒泡排序

我们将自己模拟实现的qsort函数称my_qsort,实现如下:


void my_qsort(void* base,size_t sz,size_t width,int (*cmp)(const void* p1,const void* p2))//cmp 是函数指针,在my_sort中被多次调用
{//它指向的函数是int_cmp,int_cmp就是回调函数int i = 0;for(i = 0;i < sz - 1;i++)//趟数不变{int j = 0;for(j = 0;j < sz - i - 1;j++)//每一趟进行的比较数不变{if(cmp((char*)base + j * width,(char*)base + (j + 1) * width) > 0 )//判断要改变{Swap((char*)base + j * width,(char*)base + (j + 1) * width,width);}}}
}

5.1不同类型比较

我们定义一个cmp函数来实现比较:

5.1.1cmp实参

我们传入排序的数组的首元素地址base,将base强制类型转化为 char* 类型,这样base与整数的运算就只会跨过这个整数个字节;

如果再知道每个元素的大小(长度),那么我们就可以精确的访问到每个元素了;


怎么理解呢? 

 e.g.1:对于int

 图中的base指针的位置是 j == 1的位置

 e.g.2:对于char

 

 图中的base指针的位置是 j == 5 的位置 

5.1.2cmp形参

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

由于比较的对象是整型,所以定义一个比较整形的函数,void* 类型不能直接解引用,所以将p1,p2强制类型转化,将两数相减返回。

5.2不定类型交换问题

定义交换函数Swap 

void Swap(char* buf1,char* buf2,int width)//按字节逐个交换
{for(int i = 0;i < width;i++){char tem = *buf1;*buf1 = *buf2;*buf2 = tem;buf1++;//如果是整型,则要遍历四个字节buf2++;}
}

这时候,我们就知道定义width的意义了,对于一个数据类型,我们虽然不知道它的类型,但是我们可以根据它的长度,一个一个字节地交换数据。


整体代码运行

对于所有类型,我们的冒泡排序都可以排序了,它的作用与库函数qsort一致,仍然需要我们自己写cmp函数:;

对int型数据的排序:

#include<stdio.h>
#include<string.h>
int int_cmp(const void* p1,const void* p2)
{return *(int*)p1 - *(int*)p2;
}
void Swap(char* buf1,char* buf2,int width)//按字节逐个交换
{for(int i = 0;i < width;i++){char tem = *buf1;*buf1 = *buf2;*buf2 = tem;buf1++;//如果是整型,则要遍历四个字节buf2++;}
}void my_qsort(void* base,size_t sz,size_t width,int (*cmp)(const void* p1,const void* p2))//cmp 是函数指针,在my_sort中被多次调用
{//它指向的函数是int_cmp,int_cmp就是回调函数int i = 0;for(i = 0;i < sz - 1;i++)//趟数不变{int j = 0;for(j = 0;j < sz - i - 1;j++)//每一趟进行的比较数不变{if(cmp((char*)base + j * width,(char*)base + (j + 1) * width) > 0 )//判断要改变{Swap((char*)base + j * width,(char*)base + (j + 1) * width,width);}}}
}void test1(void)
{int arr[] = {2,5,8,9,6,3,1,4,7,0};int sz = sizeof(arr)/sizeof(arr[0]);my_qsort(arr,sz,sizeof(arr[0]),int_cmp);for(int i = 0;i < sz;i++){printf("%d ",arr[i]);}
}int main()
{test1();return 0;
}

对于结构体类型,也可根据结构体内部某一元素的特征排序:

结构体类型

对结构体内的int型数据排序:


struct stu
{char name[20];int age;
};int struct_cmp_by_age(const void* p1,const void* p2)
{return ((struct stu*)p1)->age - ((struct stu*)p2)->age;
}void test2(void)
{struct stu arr[] = {{"zhangsan",18},{"lisi",25},{"wangwu",30},{"xiaoming",40}};int sz = sizeof(arr)/sizeof(arr[0]);my_qsort(arr,sz,sizeof(arr[0]),struct_cmp_by_age);for(int i = 0;i < sz;i++){printf("%s %d\n",arr[i].name,arr[i].age);}
}int main()
{test2();return 0;
}

对结构体内的char型数据排序:


struct stu
{char name[20];int age;
};int struct_cmp_by_name(const void* p1,const void* p2)
{return strcmp(((struct stu*)p1)->name,((struct stu*)p2)->name);
}void test3(void)
{struct stu arr[] = {{"zhangsan",18},{"lisi",25},{"wangwu",30},{"xiaoming",40}};int sz = sizeof(arr)/sizeof(arr[0]);my_qsort(arr,sz,sizeof(arr[0]),struct_cmp_by_name);for(int i = 0;i < sz;i++){printf("%s %d\n",arr[i].name,arr[i].age);}
}int main()
{//test1();//test2();test3();return 0;
}

1.对字符串的比较用到<string.h>中的strcmp函数,而它的返回值正好符合qsort函数第四个参数:函数的要求 

 

 

第一个字符串大于第二个,strcmp返回大于0的数,对应qsort函数要求第四个函数的返回值大于0,表示第一个参数大一第二个。 


本文回顾 

目录

一,本文目标 

二,qsort函数原型

三,冒泡排序——大数沉底,小数上浮

3.1相对于qsort,冒泡排序的局限性

四,两个问题:

4.1不定类型比较问题

4.2不定类型交换问题

五,改造冒泡排序

5.1不同类型比较

5.1.1cmp实参

5.1.2cmp形参

5.2不定类型交换问题

整体代码运行

对int型数据的排序:

结构体类型

对结构体内的int型数据排序:

对结构体内的char型数据排序:


完~

未经作者同意禁止转载

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

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

相关文章

P9 C++类

目录 01 类是什么 02 如何创建类 03 方法 后话 本期我们要讲的是 C 中的类。 我们终于讲到了面向对象编程&#xff0c;这是一种非常流行的编程方式&#xff0c;面向对象编程实际上只是一种你可以采用的编写代码的方式&#xff0c;其他语言例如 C#、Java 这些主要是面向对象…

JetLinks设备接入的认识与理解【woodwhales.cn】

为了更好的阅读体验&#xff0c;建议移步至笔者的博客阅读&#xff1a;JetLinks设备接入的认识与理解 1、认识 JetLinks 1.1、官网文档 官网&#xff1a;https://www.jetlinks.cn/ JetLinks 有两个产品&#xff1a;JetLinks-lot和JetLinks-view 官方文档&#xff1a; JetLi…

新式的拉式膜片弹簧离合器设计机械设计CAD

wx供重浩&#xff1a;创享日记 对话框发送&#xff1a;离合器 获取完整论文报告工程源文件 减震弹簧 摩擦片 膜片弹簧 压盘 轴 扭转减震器 从动盘 离合器 离合器的结构设计 为了达到计划书所给的数据要求&#xff0c;设计时应根据车型的类别、使用要求、制造条件&#xff0c;…

mac VScode 添加PHP debug

在VScode里面添加PHP Debug 插件,根据debug描述内容操作 1: 随意在index里面写个方法,然后用浏览器访问你的hello 方法,正常会进入下边的内容 class IndexController {public function index(){return 您好&#xff01;这是一个[api]示例应用;}public function hello() {phpin…

最新AI创作系统ChatGPT系统运营源码,支持GPT-4图片对话能力,上传图片并识图理解对话,支持DALL-E3文生图

一、AI创作系统 SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI创作ChatGPT&#xff1f;小编这里写一个详细图文教程吧&#xff01;本系统使用NestjsVueTypescript框架技术&#xff0c;持续集成AI能力到本系统。支持OpenAI DALL-E3文生图&#xff0c;…

【香橙派】实战记录1——简介及烧录 Linux 镜像

文章目录 一、简介1、参数2、结构3、其他配件4、下载资料 二、基于 Windows PC 将 Linux 镜像烧写到 TF 卡的方法1、使用 balenaEtcher 烧录 Linux 镜像的方法2、效果 一、简介 Orange Pi Zero 3 香橙派是一款开源的单板卡片电脑&#xff0c; 新一代的arm64开发板&#xff0c;…

2023 最新 PDF.js 在 Vue3 中的使用

因为自己写业务要定制各种 pdf 预览情况&#xff08;可能&#xff09;&#xff0c;所以采用了 pdf.js 而不是各种第三方封装库&#xff0c;主要还是为了更好的自由度。 一、PDF.js 介绍 官方地址 中文文档 PDF.js 是一个使用 HTML5 构建的便携式文档格式查看器。 pdf.js 是社区…

安装compiler version 5

这个compiler version5 在我的资源里面可以免费下载&#xff1b; 另外这个东西还需要安装&#xff0c;安装教程在这里&#xff1a;Keil最新版保姆教程&#xff08;解决缺少V5编译器问题&#xff09; - 哔哩哔哩 (bilibili.com) 看吧安装好了year

unittest指南——不拼花哨,只拼实用

&#x1f4e2;专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;交流讨论&#xff1a;欢迎加入我们一起学习&#xff01;&#x1f4e2;资源分享&#xff1a;耗时200小时精选的「软件测试」资…

Qt学习(2)

1.QObject 只有继承了QObject类的类&#xff0c;才具有信号槽的能力。所以&#xff0c;为了使用信号槽&#xff0c;必须继承QObject。凡是QObject类&#xff08;不管是直接子类还是间接子类&#xff09;&#xff0c;都应该在第一行代码写上Q_OBJECT。不管是不是使用信号槽&…

vue3之echarts渐变柱状图

vue3之echarts渐变柱状图 效果&#xff1a; 核心代码&#xff1a; <template><div class"abnormal"><div class"chart" ref"chartsRef"></div></div> </template><script setup> import * as echa…

树形 DP:树的直径

leetCode 104.二叉树的最大深度104. 二叉树的最大深度 - 力扣&#xff08;LeetCode&#xff09; class Solution { public:int maxDepth(TreeNode* root) {if(root nullptr) return 0;int lDepth maxDepth(root->left);int rDepth maxDepth(root->right);return max(l…