zoukankan      html  css  js  c++  java
  • C语言qsort函数总结

      前几天在leetcode上刷题,用qsort对二维数组进行排序,发现不会写qsort的比较函数。后面在网上找了几篇博客才弄明白,趁今天有空,对这个做一下总结,主要是以下4个方面:

    1、qsort总体介绍

    2、qsort应用于一维数组

    3、qsort应用于指针数组

    4、qsort应用于二维数组

    1、qsort总体介绍

      函数声明:void qsort(void *base, size_t nitems, size_t size, int (*compare)(const void *, const void*))

      参数解释:

        base-- 指向要排序的数组的第一个元素的指针。注意这里说是数组,所以必须是对连续的内存块进行排序。

        nitems-- 由 base 指向的数组中元素的个数

        size-- 数组中每个元素的大小,以字节为单位

        compare-- 用来比较两个元素的函数。这是qsort最难的一部分了。这里主要要注意以下2点:1、在写compare函数时,你的两个形参必须是const void *型,但是在compare函数内部你又必须将const void *类型的形参转换为实际的类型。这是我最当时最难以理解的一个问题了:我需要转换成什么类型。看了别人的总结才知道,是:指向数组元素的指针,指向数组元素的指针,指向数组元素的指针。重要的事情说三遍。具体怎么理解,会在下面的例题中详细讲,我认为这时理解qsort的一个纲领。2、comapre函数的返回值是int,即整型。记住这一点,当你在比较double类型时就不会出错了。如果返回的是一个正数。说明第一个参数排在第二个参数的后面,如果是一个负数,说明第一个参数排在第二个参数的前面。如果等于零,说明它们两是相等的,谁先谁后都没关系。

    2、qsort应用于一维数组 

    2.1、一维整型数组的用法

      举例: 

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 
     4 int compare(const void *p1, const void *p2) {
     5 
     6     return (*(int *)p1) - (*(int *)p2); // 升序排序
     7     //return (*(int *)p2) - (*(int *)p1); // 降序排序
     8 }
     9 
    10 int main(int argc, char** argv)
    11 {
    12     int i;
    13     int arr[5] = { 13, 17, 2, 7, 71 };
    14     printf("Before qsort: ");
    15     for (i = 0; i < 5; i++) {
    16         printf("%d ", arr[i]);
    17     }
    18     printf("
    ");
    19 
    20     qsort(arr, sizeof(arr)/sizeof(arr[0]), sizeof(arr[0]), compare);
    21     printf("After qsort: ");
    22     for (i = 0; i < 5; i++) {
    23         printf("%d ", arr[i]);
    24     }
    25     printf("
    ");
    26     return 0;
    27 }
    View Code

      运行结果:

      解释一下 qsort的每一个参数。qsort第一个参数是指向待排序的数组的第一个元素的指针。由于数组名作为函数的参数时,那么数组名立刻就会转化为指向该数组第一个元素的指针。因此,第一个参数我们写的是数组名arr。第二个参数是数组元素的个数,所以可以写成:sizeof(arr)/sizeof(arr[0])。第三个参数是每个元素的大小,所以可以写成 sizeof(arr[0])。我们重点来看第四个参数,即compare函数。首先compare函数的入参都写成了const void*。其次,在函数中,我们需要把入参p1,p2转换成实际的类型。什么类型? 依据纲领:指向数组元素的指针。我们的数组元素是什么?一个int(整型)。那么指向int的指针自然就是int*了。所以p1,p2在compare函数内部转换为了 (int *)p1, (int *)p2。然后再取解引用对int进行比较。

      

    2.2、一维double型数组的用法

      举例:

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #define ARRAY_LEN 6
     4 
     5 int compare1(const void *p1, const void *p2) {
     6     return (*(double *)p1) - (*(double *)p2);
     7 }
     8 
     9 int main(int argc, char** argv)
    10 {
    11     int i;
    12     double arr1[ARRAY_LEN] = {1.2, -1.2, 1.3, 33.3, 44.5, 77.5};
    13     printf("Before qsort: ");
    14     for (i = 0; i < ARRAY_LEN; i++) {
    15         printf("%.1f ", arr1[i]);
    16     }
    17     printf("
    ");
    18 
    19     qsort(arr1, sizeof(arr1)/sizeof(arr1[0]), sizeof(arr1[0]), compare1);
    20     printf("After qsort: ");
    21     for (i = 0; i < ARRAY_LEN; i++) {
    22         printf("%.1f ", arr1[i]);
    23     }
    24     printf("
    ");
    25     return 0;
    26 }
    View Code

      我以为这个程序会正确排序,但结果却啪啪打脸:

       what? 1.3 比 1.2要小?我读书虽然少,但1.3比1.2大这个常识,我还是知道的。那到底哪里出错了?没错,就是比较函数compare1的返回值写错了。比较函数的返回值类型是啥? 整型。int型。但是你看compare1中返回值都写的是啥?我返回的是两个double类型相减的类型,还是double型。这样返回的double类型就会截断(假如我返回的是 -0.1,截断后返回的就是0了,就是说1.2和1.3它们谁先谁后都没关系,那我当然是啥都不做方便啊,自然1.2就放前面了)。返回的结果就不确定了,导致出现这个错误。

      下面是修改之后的程序:

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #define ARRAY_LEN 6
     4 
     5 int compare1(const void *p1, const void *p2) {
     6     return ((*(double *)p1) > (*(double *)p2)) ? 1 : -1;
     7 }
     8 
     9 int main(int argc, char** argv)
    10 {
    11     int i;
    12     double arr1[ARRAY_LEN] = {1.2, -1.2, 1.3, 33.3, 44.5, 77.5};
    13     printf("Before qsort: ");
    14     for (i = 0; i < ARRAY_LEN; i++) {
    15         printf("%.1f ", arr1[i]);
    16     }
    17     printf("
    ");
    18 
    19     qsort(arr1, sizeof(arr1)/sizeof(arr1[0]), sizeof(arr1[0]), compare1);
    20     printf("After qsort: ");
    21     for (i = 0; i < ARRAY_LEN; i++) {
    22         printf("%.1f ", arr1[i]);
    23     }
    24     printf("
    ");
    25     return 0;
    26 }
    View Code

    运行结果:

     

    2.3、一维字符数组

      这个没啥好说的,和一维整型数组的用法类似。直接看一个例题吧:

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <string.h>
     4 
     5 int compare2(const void *p1, const void *p2) {
     6     return (*(char *)p1) - (*(char *)p2);
     7 }
     8 
     9 int main(int argc, char** argv)
    10 {
    11     char c[] = {'c', 'd', 'a', 'A', 'f', 'B', 'w', 'v', 'W', 'V'};
    12     printf("Before qsort: ");
    13     for (int i = 0; i < sizeof(c) / sizeof(c[0]); i++) {
    14         printf("%c ", c[i]);
    15     }
    16     printf("
    ");
    17 
    18     qsort(c, sizeof(c) / sizeof(c[0]), sizeof(c[0]), compare2);
    19     printf("After qsort: ");
    20     for (int i = 0; i < sizeof(c) / sizeof(c[0]); i++) {
    21         printf("%c ", c[i]);
    22     }
    23     printf("
    ");
    24     return 0;
    25 }
    View Code

      运行结果:

    3、qsort应用于指针数组

      例题:

     1 #include <stdio.h>
     2 #include <string.h>
     3 #include <stdlib.h>
     4 
     5 int compare(const void *arg1, const void *arg2) {
     6     char *a = *(char**)arg1;
     7     char *b = *(char**)arg2;
     8     return strcmp(a, b);
     9 }
    10 
    11 int main()
    12 {
    13     int i;
    14     char *arr[5] = { "i", "love", "c", "programming", "language" };
    15 
    16     qsort(arr, sizeof(arr)/sizeof(arr[0]), sizeof(char *), compare);
    17     for (i = 0; i < 5; i++) {
    18         printf("%s ", arr[i]);
    19     }
    20     printf("
    ");
    21     return 0;
    22 }
    View Code

      我们还是来分析一下为什么arg1的类型是 char **。根据纲领:指向数组元素的指针我们首先需要确定数组元素的类型。这里数组元素的类型是是 char *。所以,arg1的类型就是指向char*的指针,即char**。

      运行结果:

    4、qsort应用于二维数组

      例题:

     1 #include<stdio.h>
     2 #include<stdlib.h>
     3 #include<string.h>
     4 
     5 int cmp1(const void *arg1, const void *arg2)
     6 {
     7     return strcmp((*((char (*)[])arg1)), (*((char (*)[])arg2)));
     8 }
     9 
    10 int cmp2(const void *arg1, const void *arg2)
    11 {
    12     return strcmp((char *)arg1, (char *)arg2);
    13 }
    14 
    15 int main(int argc, char **argv)
    16 {
    17     int i;
    18     char str1[4][8] = {"java", "c", "python", "peal"};
    19     printf("COMPARE-FUNCTION-1: ");
    20     qsort(str1, 4, sizeof(str1[0]), cmp1);
    21     for (int i = 0; i < 4; i++) {
    22         printf("%s ", str1[i]);
    23     }
    24     printf("
    ");
    25 
    26     char str2[4][8] = {"java", "c", "python", "peal"};
    27     printf("COMPARE-FUNCTION-2: ");
    28     qsort(str2, 4, sizeof(str2[0]), cmp2);
    29     for (int i = 0; i < 4; i++) {
    30         printf("%s ", str2[i]);
    31     }
    32     printf("
    ");
    33     return 0;
    34  }
    View Code

      这一段代码刚开始我也是挺疑惑的,为什么compare函数的入参是这个类型的。其实只要记住qsort的纲领:指向数组元素的指针就很容易理解了。首先来看cmp1,我们待排序的是一个二维数组,也就是数组的数组,那么我们这个数组元素的类型就是一个数组类型了(char []类型)。cmp1的参数就是指向一个数组类型的指针了。指向一个char []数组类型的指针当然就是用char (*)[]表示了。

      接下来分析cmp2。我们将str2传入qsort函数,qsort函数将str2理解为指向数组第一个元素的指针,str2的第一个元素是str2[0][0],所以参数arg1和arg2指的是指向"a[i][0]"的指针,我们知道,a[i][0]是字符,就是char,所以arg1和arg2指的是char *。

      运行结果:

    参考文献:

    http://www.cppblog.com/Joe/archive/2010/10/29/131746.aspx?opt=admin

    https://www.cnblogs.com/nipan/p/4119714.html

  • 相关阅读:
    北京最好的体检中心
    算法初级面试题04——递归/非递归遍历二叉树、直观打印二叉树、寻找后继前驱节点、序列化/反序列化、折纸问题、判断是否平衡/搜索/完全二叉树、求完全二叉的节点数
    算法初级面试题03——打印链表公共部分、判断链表是否为回文、按值划分链表为小于等于大于、复制随机指针链表、两链表相交判断的一系列问题
    算法初级面试题03——队列实现栈、栈实现队列、转圈打印矩阵、旋转矩阵、反转链表、之字打印矩阵、排序矩阵中找数
    算法初级面试题02——荷兰国旗问题、随机快速排序、堆排序、桶排序、相邻两数的最大差值问题、工程中的综合排序算法面试题
    算法初级面试题01——认识时间复杂度、对数器、 master公式计算时间复杂度、小和问题和逆序对问题
    JAVAEE——SpringBoot日志篇:日志框架SLF4j、日志配置、日志使用、切换日志框架
    JAVAEE——SpringBoot配置篇:配置文件、YAML语法、文件值注入、加载位置与顺序、自动配置原理
    JAVAEE——SpringBoot入门:简介、微服务、环境准备、helloworld与探究、快速构建项目
    JAVAEE——宜立方商城14:项目部署规划、Tomcat热部署、反向代理的配置
  • 原文地址:https://www.cnblogs.com/LydiammZuo/p/11779918.html
Copyright © 2011-2022 走看看