qsort使用详解

qsort

简介

  qsort()是一个排序函数,其内部实现采用的是“快排”,时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn),性能远好于冒泡排序( O ( n 2 ) O(n^2) O(n2))。而且可以自定义排序条件,只要理解了qsort(),你就会永远抛弃冒泡。

  如果想要学习快排的思想,可以参考:1.6 快速排序 | 菜鸟教程 (runoob.com);

  本次串讲中,我们只需要知道:qsort()能够用一种特殊的方式来排序,我们可以手动写cmp函数来控制qsort()的结果。假设对数组a[100]排序,现有 a [ i ] , a [ j ] 且 i < j a[i],a[j]且ia[i],a[j]i<j,程序会将 a [ i ] 和 a [ j ] a[i]和a[j] a[i]a[j]放到cmp函数中比较,如果cmp返回值为正数,二者交换位置,返回值为负,二者位置不变。返回值为0,则随机。

void qsort(
    void *base,
    size_t nmemb,
    size_t size,
    int (*compar)(const void *, const void *)
    );
//以下解释摘自菜鸟教程
//base -- 指向要排序的数组的第一个元素的指针。
//nitems -- 由 base 指向的数组中元素的个数。
//size -- 数组中每个元素的大小,以字节为单位。
//compar -- 用来比较两个元素的函数。

每个参数的意义

  第一个参数base,为需要排序的空间的起始地址。

int a[100];
qsort(a  , nmemb,size,cmp);	//表示从a[0]开始排序
qsort(a+1, nmemb,size,cmp);	//表示从a[1]开始排序

  第二、三个参数要合在一起看才好理解。

  第三个参数size,表示每个“排序单元”所占的字节个数,此处“排序单元”意为排序中需要交换时的单元。

  第二个参数nmemb,表示一共有多少个“排序单元”参与排序。

  size和nmemb的乘积即为需要排序的空间所占的字节数

int a[nmemb];
double b[nmemb];
qsort(a, nmemb   , 4, cmp);	//一个int所占空间为4个字节
qsort(b, nmemb   , 8, cmp);	//一个double所占空间为8个字节
qsort(a, nmemb/2 , 8, cmp);	//如果这么写,意味着将两个int型变量打包起来,视为一个对象看待。排序中需要交换时会一次换两个int
//第1,3中写法的排序范围都是a[0]~a[nmemb-1],只是解读的方法不一样

  第4个参数是理解qsort的关键,其将用于比较两个元素,通过返回值来确定需不需要交换两个元素。

  需要明确一个概念,对于一个compare函数,最重要的就是返回值。函数的实现过程不重要,只要能够得到正确的返回值就行。为了得到正确的返回值,定义中间变量、调用其它函数之类的都是可以的。

基本用法

举个例子:

int cmp(const void *a,const void *b)  
{
	//为什么有const?const限定一个变量不允许被改变,即只可读。对于cmp这个函数,只需要读指针中的内容确认返回值即可。
    //为什么是void?void型指针意为“无类型”指针。这是因为程序不知道排序的单元为int还是double还是我们自己人为凑出来的类型。
  	//第一步,明确需要排序单元类型
    int *aa=(int *)a;
    int *bb=(int *)b;
    //第二步,比较大小确定返回值
    if(*aa>*bb)	return 1;	//aa指向的元素在b指向的元素之前,目的是升序排序,如果a中的值大,则交换二者,返回正数
    return -1;			   //反之则不交换,返回负数。(相等时不需交换)
}
int main()
{
    int a[100];
    qsort(a,100,sizeof(a[0]),cmp);//升序排序
}

进阶

  为什么在解释第三个参数时需要创造出“排序单元”这一定义?是因为这样理解可以方便理解qsort的本质,在使用qsort排序结构体和自己凑的类型时更从容。

  例题:现在有一个二维数组 a [ 100 ] [ 3 ] a[100][3] a[100][3],记录了一百个学生的分数,学号,年龄。现在需要你按照学号升序排序。

int cmp(const void *a,const void *b)  
{
    //第一步,明确要比较对象类型,本题中为“学号”,用int表示
    int *aa=(int *)a;	//aa此时为某个排序单元 (a[i][0],a[i][1],a[i][2]) 的起始地址,即指向了a[i][0]。
    int *bb=(int *)b;	
  	//第二部,将指针指向需要比较的对象
    aa=aa+1;	//根据学号排序,即根据a[i][1]排序。需要修改指针aa指向的地址,让其从指向a[i][0]变为指向a[i][1]
    bb=bb+1;
    //第三步,比大小,确定返回值
    if(*aa>*bb)	return 1;
    return -1;
}

int main(){
    int a[100][3];
    //输入,不写了,假设输完了
    qsort(a,100,12,cmp);//有100个排序单元,每个单元12字节(3个int)。也可以写成 sizeof(a[0]) 或者 3*sizeof(int) 
}

写在后面

  本文没有介绍如何使用qsort对结构体进行排序,想要了解相关内容可以移步我的另一篇文章->结构体的应用 和 结构体排序(qsort)

你可能感兴趣的:(C语言基础,排序算法,算法,数据结构)