冒泡排序模拟qsort函数

news2025/2/28 8:12:57

欢迎来到 Claffic 的博客 💞💞💞

前言:

学习C语言,一般情况下都会接触到冒泡排序,你知道吗,用冒泡排序的思想可以模拟实现qsort函数(库函数的一种,可以实现快排)跟我来看看吧。

注:此博客包含进阶知识,建议学完C语言初阶知识再进行学习哦 ~  


1. qsort 函数介绍

打开你的 C/C++资源网站/软件,搜索 qsort 函数  cplusplus - qsort 

  可以解读出:

qsort 函数的功能是排序数组中的元素

qsort 函数不返回数据;

使用 qsort 需要传递四个参数

传递的四个参数大有讲头,这里详细解释:

先来看看前三个:

base: 直译是 “指向数组中要排序的第一个对象的指针,转换为 void* ”。

简单说就是 要排序,你得先把你要排序的数组给我 ,那为什么要转换为 void* 呢?

这里就不得不说 void* 的利弊 了:

好处:void* 可以接收任意指针类型,包容性极强;

缺陷:接收的指针不可直接使用,也就意味着使用前要进行类型转换

原因 就是 qsort 函数的作者不知道你要传递什么类型的数组,所以干脆就用覆盖范围最广的喽 ~

num: 直译是 “ 数组中由base指向的元素个数,size_t 是无符号整型 ” 。

这个比较好理解,就是数组中元素总和size_t 就是 unsigned int ,因为总和不可能为负数,类型是无符号整型也理所应当了。

size: 直译是 “ 数组中每个元素的字节大小 ” 。 

这个倒是好理解,但你知道它有什么用吗?嘿嘿,这里留个小悬念 ~

看最后一个:

这里就直接解释吧:

int (*compar)(const void*,const void*) 是函数指针,它叫做 compar

它指向的函数 需要两个参数(都是 void* 型,且指向不可变);

它指向的函数 返回整型数值。

那就是要给 qsort 传个函数呗,但传递的这个函数的作用是?

你想啊,假如我用冒泡排序的思想给一个整数数组排升序,是不是先要比较相邻两个元素的大小 在来决定是否进行交换

是的,比较两个元素就是这个函数的作用,这两个元素可以是相邻两个元素(注意:qsort 函数比较的不一定是两个相邻的元素,因为我们用冒泡排序的思想,所以可以理解为两个相邻的元素),指向它们的指针类型同样是 void* ,最广泛。

返回值呢?

简单来说,可以用两个元素做差来解释:

  a - b (a , b 都是整型)

a > b ,返回大于 0 的整数;

a = b ,返回 0 ;

a < b ,返回小于 0 的整数。

2.实现过程

充分认识了 qsort ,接下来就是用冒泡排序的思想来模拟它,

我们先来复习一遍冒泡排序:

//普通的冒泡排序:
void bub_sort(int* arr, int sz)
{
    for (int i = 0; i < sz - 1; i++)
    {
        for (int j = 0; j < sz - 1 - i; j++)
        {
            if (arr[j] > arr[j + 1])
            {
                int temp = 0;
                temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
}

在这个冒泡排序中,只能实现整型类型元素的排序;

我们的目标是实现多种类型元素的排序

所以要改善的部分是判断与交换的部分

我们先来写出框架:

void bubble_sort(void* base, size_t num, size_t size, int (*cmp)(const void* e1, const void* e2))
{
    for (int i = 0; i < num - 1; i++)
    {
        for (int j = 0; j < num - 1 - i; j++)
        {
            if (cmp()>0)
            {
                //交换
                Swap();
            }
        }
    }

}

接下来就是实现 cmp 函数Swap 函数

2.1 cmp 函数部分

如果传参类型是 整型:
直接返回差值就好。

int int_cmp(const void* e1, const void* e2)
{
    return *(int*)e1 - *(int*)e2;
    //强制类型转换
}

如果传参类型是包含整型和字符串类型的 结构体:

先把结构体创建:

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

struct Stu s[3] = { {"zhangsan",20},{"lisi",60},{"wangwu",30} };

如果年龄排序结构体:

int age_cmp(const void* e1, const void* e2)
{
    return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
    //莫忘记强制类型转换  指向具体结构体成员
}

如果名字排序结构体:

实际上是字符串的比较,我们很容易想到 strcmp 函数,将比较的两个字符串传入函数即可。

int name_cmp(const void* e1, const void* e2)
{
    return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}

 2.2 Swap 函数部分

Swap 函数就是执行 交换相邻元素 任务的

 既然两个数组的元素不能直接交换,那么我们就开辟一块小空间,通过这块小空间交换一个元素后再进行下一个元素的交换。

理应,Swap 函数需要指向数组第一个元素的指针,还有数组中每个元素的字节大小(揭开悬念)

代码:

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

 到这里基本上就大功告成啦

2.3函数整体代码

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

//交换函数
void Swap(char* buf1, char* buf2, int size)
{
    for (int i = 0; i < size; i++)
    {
        char temp = *buf1;
        *buf1 = *buf2;
        *buf2 = temp;
        buf1++;
        buf2++;
    }
}

//冒泡排序思想实现指定类型数组的排序
void bubble_sort(void* base, size_t num, size_t size, int (*cmp)(const void* e1, const void* e2))
{
    for (int i = 0; i < num - 1; i++)
    {
        for (int 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* e1, const void* e2)
{
    return *(int*)e1 - *(int*)e2;
}

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

int age_cmp(const void* e1, const void* e2)
{
    return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
int name_cmp(const void* e1, const void* e2)
{
    return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}

//测试部分
void int_test()
{
    int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
    int sz = sizeof(arr) / sizeof(arr[0]);
    bubble_sort(arr, sz, sizeof(arr[0]), int_cmp);

    for (int i = 0; i < sz; i++)
    {
        printf("%d ", arr[i]);
    }
}
void struct_test()
{
    struct Stu s[3] = { {"zhangsan",20},{"lisi",60},{"wangwu",30} };
    int sz = sizeof(s) / sizeof(s[0]);
    bubble_sort(s, sz, sizeof(s[0]), age_cmp);

}

int main()
{
    int_test();
    struct_test();
    return 0;
}

已上传至 我的gitee ,代码拿走不谢 ~


总结:

这篇博客带大家用冒泡排序的思想模拟了 qsort 函数,应用到了 函数回调 的知识点,属于指针进阶知识,你学会了吗?

如果你觉得这篇文章还不错并且对你有帮助,不妨支持一波哦  💗💗💗

关注我  不迷路!!! 

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

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

相关文章

图解面试题:经典50题!掌握这些题,面试也太简单了!

已知有如下4张表&#xff1a;学生表&#xff1a;student(学号,学生姓名,出生年月,性别)成绩表&#xff1a;score(学号,课程号,成绩)课程表&#xff1a;course(课程号,课程名称,教师号)教师表&#xff1a;teacher(教师号,教师姓名)1.汇总分析-查询学生的总成绩并进行排名/* 【知…

CSS基础知识(盒子模型)

继承上一篇CSS的三大特性的优先级继续讲解。 1.1优先级 优先级注意点&#xff1a; 权重是有4组数字组成的&#xff0c;但是不会有进位。可以理解为类选择器永远大于元素选择器&#xff0c;id选择器永远大于类选择器以此类推。等级判断从左向右&#xff0c;如果某一位数值相同…

前端学习之CSS基础

前言 html标签就不说了&#xff0c;这次学习CSS样式&#xff0c;就是美化html标签。 快速了解什么是css 普通标签&#xff1a; 加了css样式&#xff1a; <img src"https://static.runoob.com/images/icon/mobile-icon.png" style"height:100px" /&…

No module named ‘pycocotools’

网上搜了之后 按照网上的去做 全都无果 开始自己探索 原本我pycharm里选的环境是 python3.8 (pytorch)winR输入cmd进去后 输入 python –V返回的是本地python版本2.7 所以我当前系统python版本和我pycharm里选的不一样 然而pycocotools这个包本质上应该是安装在我pycharm里…

Referer;盗链;防盗链的工作原理

目录 Referrer-policy 如何设置referer 盗链 防盗链的工作原理 绕过图片防盗链 设置meta 设置referrerpolicy"no-referrer" 客户端在请求时修改header头部 利用https网站盗链http资源网站&#xff0c;refer不会发送 常见防盗链方法 利用nginx 服务器端判…

python基础语法一

一、变量 1.1、变量定义 变量就是可变的量&#xff0c;对于一些有可能会经常变化的数据&#xff0c;我们需要使用一个符号&#xff0c;这样才能计算中使 用它&#xff0c;就像我们在小学时学过的一元方程中的"x"一样。比如说&#xff0c;我们在控制台内输入&#xf…

Java 集合List接口介绍和使用

List接口的基本介绍 1.List接口是Collection的子接口 2.List中的元素都是有序的除了LinkedList。 一些实用的方法 1.add()添加元素 2.get()得到指定位置的元素 3.addAll&#xff08;&#xff09;追加一个List 4.indexOf&#xff08;&#xff09;返回元素的位置 5.remove…

人工智能 -多任务编程、进程、线程介绍

目录1&#xff0c; 多任务的概念2&#xff0c;进程2.1进程的介绍2.2多进程完成多任务2.3进程执行带有参数的任务2.4获取进程编号2.5进程间不共享全局变量2.6主进程和子进程的结束顺序3、线程3.1多线程完成多任务3.2线程执行带有参数的任务3.3主线程和子线程的结束顺序3.4线程中…

连续子数组的最大和(从暴力理解到DP)

连续子数组的最大和题目思路暴力解题思路画出矩阵进行分析确定转移方程DP代码题目 思路 从leetcode上看到的题解&#xff0c;突然恍然大悟&#xff0c;之前不容易理解转移方程终于理解了&#xff0c;这个思路真的对新手很友好&#xff0c;现在出一个C版本&#xff0c;而且&…

谷歌出品,数据集搜索引擎上线了!

文 | 小戏记得在刚入门 ML 时&#xff0c;希望找到一个关于特定领域下的数据集&#xff0c;涉世未深的我在中文互联网不断搜索&#xff0c;可每每点进链接出来的都是某 SDN 下载的高价勒索。用惯了直接从老师同学那里讨来的数据集的我第一次感受到了“寻找数据集”这样一个简单…

【并查集】实现思路及例题

一、应用场景 用于处理不相交集合的合并和查询问题 示例&#xff1a; n 个元素&#xff08;分属不同的的 n 个集合&#xff09;&#xff0c;进行两种操作&#xff1a; 并 —— 给出两个元素的关系&#xff0c;合并两个集合查 —— 查询两个元素是否在同一个集合 二、并查集…

「数据密集型系统搭建」原理篇|用什么方式存储数据最合适

本篇来聊聊数据存储的内容&#xff0c;看看程序世界里数据是以什么形式存在的&#xff1f;为了描述数据并把它们和这个现实世界关联起来我们一般都是如何去进行表达的&#xff1f;最后通过我们习惯的表达方式再结合数据结构是如何存储下来的&#xff1f; 在进行技术方案设计的时…

分享102个PHP源码,总有一款适合您

PHP源码 分享117个PHP源码&#xff0c;总有一款适合您 PHP源码下载链接&#xff1a;https://pan.baidu.com/s/1Ike0x99BcMfZPy6tFSpM9w?pwdzqem 提取码&#xff1a;zqem import os from time import sleepimport requests from bs4 import BeautifulSoup from docx import D…

Linux 系统Bash的常用功能

了解了基本的Linux文件文件系统的概念后,我们将更深入的了解一下Linux的其他方面的内容,那就是我们所使用的用户接口,也就是大家常听到的 Shell ,是一种Linux的命令接口,在 Linux 的世界中,默认使用的是 GNU 开发出来的 shell ,称为 BASH Shell,简单来说,我们之前使用的几个命令…

10.JS笔记-对象

1、什么是对象 对象是一个具体的事物&#xff0c;在js中&#xff0c;对象是一组无序的属性和方法的集合 属性&#xff1a;事物的特征 方法&#xff1a;事物的行为 2、创建对象 利用字面量创建对象利用new Object创建对象利用构造函数创建对象 2.1 变量、属性和方法、函数的…

人工智能的核心技术是什么?

&#xff08;本文阅读时间&#xff1a;5分钟&#xff09;人工智能的核心技术是它的算法被广泛认可的「算法」专业定义是&#xff1a;算法是模型分析的一组可行的&#xff0c;确定的&#xff0c;有穷的规则。基于规则的人工智能上个世纪六七十年代出现的早期人工智能系统都是基于…

VueJs中如何自定义hooks(组合式)函数

前言在Vue当中,一个非常重要的功能就是组件的复用,编写Vue组件,更多的也是在拼装组件,将页面的各个功能进行模块化便于维护和管理,而在项目里,有些页面中的组件的逻辑功能是一样的,如果没有进行功能逻辑的复用,那么每个页面都需要重复的写一遍在Vue当中各个组件是保持独立的,如…

一份职业游戏3D建模师日常工作流程列表,看完不信还有人说建模门槛低

随着游戏行业的发展&#xff0c;越来越多的人开始对这个行业感兴趣&#xff0c;因此有很多的小伙伴梦想成为一个游戏模型师&#xff0c;成为游戏行业里的一员。但是很多人都对这个工作具体是做什么的并不是很了解&#xff0c;下面&#xff0c;我们就来说说游戏模型师的主要工作…

Word处理控件Aspose.Words功能演示:使用 C# 将 DOCX 转换为 HTML

Aspose.Words 是一种高级Word文档处理API&#xff0c;用于执行各种文档管理和操作任务。API支持生成&#xff0c;修改&#xff0c;转换&#xff0c;呈现和打印文档&#xff0c;而无需在跨平台应用程序中直接使用Microsoft Word。此外&#xff0c; Aspose API支持流行文件格式处…

Javascript:Class构造函数

为什么需要class 在其他语言中class已经是一个早就被实现的功能&#xff0c;在JavaScript中一直到ES6被实现。在class没有实现之前我们是这样写的&#xff08;如下代码&#xff09; function Person(name,sex){this.name this.sex } Person.prototype.sayfunction(){alert(h…