C语言:深入浅出qsort方法,编写自己的qsort完成冒泡排序

目录

什么是qsort? 

函数原型

比较函数 compar

排序整型数组

排序结构体数组

根据成员字符排序

 strcmp函数

根据成员整型排序

自定义qsort实现冒泡排序 

qsort的实现原理 

具体步骤

快速排序示例代码: 


什么是qsort? 

qsort是 C 语言标准库<stdib.h>中的一个函数,用于对数组进行快速排序,如下 

  • base:指向需排序的数组指针(同数组首元素地址)
  • num:数组中的元素个数。
  • size:每个元素的大小(以字节为单位)。
  • compar:比较函数指针,用于定义排序顺序。

函数原型

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

比较函数 compar

比较函数 compar 负责定义数组元素的排序顺序。它应该接受两个参数,每个参数的类型都是 const void *,并返回一个整型值。比较函数的返回值定义了元素之间的排序关系:

  • 如果返回值小于 0,则第一个参数应该排在第二个参数之前。
  • 如果返回值等于 0,则两个参数的相对位置不变。
  • 如果返回值大于 0,则第一个参数应该排在第二个参数之后。

排序整型数组

void是无具体类型的指针,可以接受任意类型的地址,但是不能解引用,也不能+-操作,因为它不知道自己有几个字节的权限

int a = 10;
void *pv = &a;

compare函数形参中:因为指针类型是void,不能解引用,需要强制类型转换为int再解引用(根据自己的数据类型来决定是int还是char还是结构体)

#include <stdio.h>
#include <stdlib.h>

int compare(const void* a, const void* b) {
    return (*(int*)a - *(int*)b);
}

int main() {
    int arr[] = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};
    int size = sizeof(arr) / sizeof(arr[0]);
    
    qsort(arr, size, sizeof(int), compare);

    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]); // 1 1 2 3 3 4 5 5 5 6 9
    }
    return 0;
}

排序结构体数组

根据成员字符排序

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

int cmp_struct_name(const void *e1, const void *e2)
{
    return strcmp(((struct Stu *)e1)->name, ((struct Stu *)e2)->name); // lisi、wangwu、zhangsan
}

    
struct Stu s[] =
    {{"zhangsan", 22},
    {"lisi", 24},
    {"wangwu", 23}};
int struct_size = sizeof(s) / sizeof(s[0]);
qsort(s, struct_size, sizeof(s[0]), cmp_struct_name);

 strcmp函数

  • strcmp函数用于比较字符串大小 ——> ==0 >0 <0
  • strcmp() 函数比较字符串时会逐个字符地比较它们的 ASCII 码值,直到有字符不相同时才停止比较。
  • 如果其中一个字符串已经结束了,而另一个字符串还没有结束,那么这个字符串就被认为比另一个字符串更小。
  • 头文件<string.h>

根据成员整型排序

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


int cmp_struct_age(const void *e1, const void *e2)
{
    /* 从小到大 */
    return ((struct Stu *)e1)->age - ((struct Stu *)e2)->age; // 22 23 24
}

struct Stu s[] =
    {{"zhangsan", 22},
    {"lisi", 24},
    {"wangwu", 23}};
int struct_size = sizeof(s) / sizeof(s[0]);
qsort(s, struct_size, sizeof(s[0]), cmp_struct_age);

自定义qsort实现冒泡排序 

/* 最终交换字节 */
void Swap(char *buf1, char *buf2, int width)
{
    for (int i = 0; i < width; i++)
    {
        char tmp = *buf1;
        *buf1 = *buf2;
        *buf2 = tmp;
        buf1++;
        buf2++;
    }
}

void my_qsort(void *base, int sz, int width, int (*cmp)(const void *e1, const void *e2))
{
    for (int i = 0; i < sz - 1; i++)
    {
        for (int j = 0; j < sz - 1 - i; j++)
        {
            /*
                *实参传进去的是待比较的元素的地址
                之所以用char类型指针转换,是因为最保险,什么类型指针能访问的字节大小都可以从最小字节1开始活动
                比如这里,数据是int类型,那么cmp的实参第一个是 0*4,第二是 1*4,即第一个int数的地址,第二个int数的地址
             */
            if (cmp((char *)base + j * width, (char *)base + (j + 1) * width) > 0)
            {
                Swap((char *)base + j * width, (char *)base + (j + 1) * width, width);
            }
        }
    }
}

   
my_qsort(arr, sz, sizeof(arr[0]), cmp_int);
for (int i = 0; i < sz; i++)
{
   printf("%d ", arr[i]); // 9 8 7 6 5 4 3 2 1 0
}

qsort的实现原理 

qsort方法,它使用了一种叫做快速排序(Quicksort)的算法。快速排序是一种分治的排序算法,其基本思想是选择一个基准值,然后将数组中小于基准值的元素放到基准值的左边,大于基准值的元素放到基准值的右边,最终使得基准值左边的所有元素都小于等于基准值,右边的所有元素都大于等于基准值。然后递归地对基准值两边的子数组进行排序,直到整个数组有序为止。

具体步骤

  1. 选择基准值:从数组中选择一个元素作为基准值(通常选择第一个或者中间的元素)。
  2. 分区过程:将数组中的元素重新排列,将小于或等于基准值的放在基准值的左边,大于基准值的放在右边。
  3. 递归排序:对基准值左右两个子数组分别递归执行上述步骤。

快速排序示例代码: 

void quicksort(int arr[], int low, int high) {
    if (low < high) {
        int pivot = partition(arr, low, high);
        quicksort(arr, low, pivot - 1);
        quicksort(arr, pivot + 1, high);
    }
}

int partition(int arr[], int low, int high) {
    int pivot = arr[low];
    int i = low, j = high;
    while (i < j) {
        while (i < j && arr[j] >= pivot) {
            j--;
        }
        arr[i] = arr[j];
        while (i < j && arr[i] <= pivot) {
            i++;
        }
        arr[j] = arr[i];
    }
    arr[i] = pivot;
    return i;
}

在这个示例中,quicksort 函数进行了递归排序,partition 函数则负责进行分区操作。具体而言,partition 函数以第一个元素作为基准值,然后对数组进行分区操作,并返回新的基准值的位置。对两个子数组进行的递归排序最终能够完成整个数组的排序过程。

快速排序的时间复杂度为 O(n log n),这使得快速排序成为一种非常高效的排序算法。在最好情况下,快速排序的时间复杂度是 O(n log n),而在最坏情况下,时间复杂度为 O(n^2),但平均情况下时间复杂度依然是 O(n log n)。当然,这也取决于选择的基准值和数组的初始状态。

同时,快速排序是一种原地排序算法,它不需要额外的存储空间来存储临时数据,只需要对原始数组进行原地交换和重排列。

总结来说,快速排序通过选择基准值、分区和递归排序等步骤,能够高效地对数组进行排序,在平均情况下的时间复杂度为 O(n log n),适用于大多数场景下的排序需求。因此,qsort 函数使用的快速排序算法在实际应用中具有较高的效率和性能表现。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

YOLO目标检测——交通标志分类数据集【含对应voc、coco和yolo三种格式标签】

实际项目应用&#xff1a;交通标志识别数据集在自动驾驶、交通安全监控、智能交通系统、驾驶员辅助系统和城市规划等领域都有广泛应用的潜力数据集说明&#xff1a;交通标志分类数据集&#xff0c;真实场景的高质量图片数据&#xff0c;数据场景丰富&#xff0c;含多场景白天黑…

OOM排查

OOM排查 一&#xff0c;原因 1.一次性申请对象太多&#xff0c;创建了大量对象&#xff0c;尤其从表中读取了大量数据&#xff0c;循环中大量创建对象&#xff0c;放入list中。方案&#xff1a;限量 2.内存资源耗尽为释放&#xff0c;如connction&#xff0c;线程。方案&#…

猫罐头什么牌子好?2023营养又美味的猫主食罐头推荐!

亲爱的猫咪主人&#xff0c;你是否为你家小猫咪的挑食问题感到困扰&#xff1f;作为一位在宠物店工作了七年&#xff0c;负责喂养三十多只猫咪的店长&#xff0c;我对许多品牌的猫罐头都非常熟悉了。对于猫罐头哪个牌子好这个问题&#xff0c;我想借此机会分享一些见解。 在本…

软约束与硬约束

软约束硬约束 软约束硬约束 硬约束优化 1.基于走廊的光滑轨迹生成 2.基于贝塞尔曲线的轨迹优化 软约束优化 1.基于距离的轨迹优化 2.目标函数的设计 目标函数 光滑代价函数 碰撞代价函数 动力学代价函数。 光滑代价函数&#xff1a; 使用minimum snap来实现。 碰撞…

lua中的循环 while、for、repeat until三种循环方式、pairs和ipairs区别

lua中的循环 while、for、repeat until三种循环方式、pairs和ipairs区别 介绍for循环参数ipairs和pairs whilerepeat until总结 介绍 这里我用while、for、repeat until分别输出1-20之间的奇数 &#xff0c;具体的语法可以看下面的代码 for循环 参数 定义一个初始值为start…

毫米波雷达技术的医疗创新:开启无创检测与监测的新时代

随着科技的不断进步&#xff0c;毫米波雷达技术正日益成为医疗领域的一项引人注目的创新。其无创性质、高分辨率和多功能性为医学诊断和监测带来了新的可能性。本文将深入探讨毫米波雷达技术在医疗创新中的应用&#xff0c;着眼于无创检测与监测领域的突破性发展。 1. 毫米波雷…

Babylonjs学习笔记(八)——网格行为

书接上回&#xff0c;这里讨论MeshAction网格行为&#xff01;&#xff01;&#xff01; 一、搭建基础场景 let box:AbstractMesh; let cube:AbstractMesh; let sphere:AbstractMesh; let cylinder:AbstractMesh; let mat:PBRMaterial;// 创建天空盒 const createSkyBox (sc…

如何在在线Excel文档中规范单元格输入

在日常的工作中&#xff0c;我们常常需要处理大量的数据。为了确保数据的准确性和可靠性。我们需要对输入的数据进行规范化和验证。其中一个重要的方面是规范单元格输入。而数据验证作为Excel中一种非常实用的功能&#xff0c;它可以帮助用户规范单元格的输入&#xff0c;从而提…

简单代理模式

代理模式 代理模式(Proxy)&#xff0c;为其他对象提供一种代理以控制对这个对象的访问。 结构图如下&#xff1a; ISubject接口&#xff0c;定义了RealSubject和Proxy的共用接口方法&#xff0c;这样就可以在任何使用RealSubject的地方使用Proxy代理。 ISubject接口 public…

JavaScript使用正则表达式

正则表达式(RegExp)也称规则表达式(regular expression)&#xff0c;是非常强大的字符串操作工具&#xff0c;语法格式为一组特殊字符构成的匹配模式&#xff0c;用来匹配字符串。ECMAScript 3以Perl为基础规范JavaScript正则表达式&#xff0c;实现Perl 5正则表达式的子集。Ja…

小米手机怎么识别图片上的表格文字?

前言&#xff1a; 小米手机怎么识别图片上的文字&#xff1f;有二种解决方案&#xff1a;一是直接用系统自带的OCR工具来实现&#xff0c;二是借助第三方软件&#xff08;如金鸣识别&#xff09;来实现。 一、用小米自带的系统工具来实现。 对于MIUI12.5以上系统的小米手机来…

学C++跟着视频学还是跟着书学?

学C跟着视频学还是跟着书学&#xff1f; 感觉得看基础和目标 如果不是喜欢 C 或者以求职 / 完成 C 相关工作为目标的话&#xff0c;菜鸟教程其实都够了&#xff0c;基本语法掌握就差不多&#xff0c;然后多去写。 最近很多小伙伴找我&#xff0c;说想要一些C的资料&#xff0…

如何在在线Excel文档中对数据进行统计

本次我们将用zOffice表格的公式与数据透视表分析样例&#xff08;三个班级的学生成绩&#xff09;。zOffice表格内置了大量和Excel相同的统计公式&#xff0c;可以进行各种常见的统计分析&#xff0c;如平均值、标准差、相关性等。同时&#xff0c;zOffice也有数据透视表功能&a…

【Web】在前端中,HTML<meta>标签

<meta>实例 <head><meta name"description" content"免费在线教程"><meta name"keywords" content"HTML,CSS,XML,JAVASCRIPT"><meta name"author" content"runoob"><meta char…

java高级之单元测试、反射

1、Junit测试工具 Test定义测试方法 1.被BeforeClass标记的方法,执行在所有方法之前 2.被AfterCalss标记的方法&#xff0c;执行在所有方法之后 3.被Before标记的方法&#xff0c;执行在每一个Test方法之前 4.被After标记的方法&#xff0c;执行在每一个Test方法之后 public …

相亲交友小程序源码 同城相亲交友小程序源码

相亲交友小程序源码 同城相亲交友小程序源码 收费模式&#xff1a; 1、会员开通VIP收费&#xff1b;3、会员购买服务项目收费&#xff08;可以自定义服务项目&#xff09;&#xff1b; 二、全民推广系统&#xff1a; 1、邀请用户注册奖励&#xff08;邀请一个用户进入注册…

一个数组实现两个栈

一个数组实现两个栈 基本思路&#xff1a; 1.定义两个栈顶top1-1&#xff0c;top2maxsize 2.栈满&#xff0c;当top1与top2相差1时栈满 package 例题; //一个数组实现两个栈 public class TwoStack {private int[] arr;private int maxSize;//定义栈顶private int top1;private…

C# TCP Server服务端多线程监听RFID读卡器客户端上传的读卡数据

本示例使用设备介绍&#xff1a;液显WIFI无线网络HTTP协议RFID云读卡器可编程实时可控开关TTS语-淘宝网 (taobao.com) using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using Sy…

mindspore mindcv图像分类算法;模型保存与加载

参考&#xff1a; https://www.mindspore.cn/tutorials/en/r1.3/save_load_model.html https://github.com/mindspore-lab/mindcv/blob/main/docs/zh/tutorials/finetune.md 1、mindspore mindcv图像分类算法 import os from mindcv.utils.download import DownLoad import o…

移远EC600U-CN开发板 day03

控件探索-按钮&#xff08;lv.btn&#xff09; (1) 创建并显示一个按钮 * 核心代码 btn lv.btn(scr) #将按钮与src对象关联 btn.align(lv.ALIGN.CENTER,0,0) #居中显示&#xff08;第1个0表示x的偏移量&#xff0c;第2个0表示相对于y的偏移量&#xff09; label lv.l…