C语言中在一维数组,二维数组,结构体下qsort使用

qsort使用前提

  • 首先包含头文件:#include <stdlib.h>
  • 其次需要cmp比较函数:(自己编)

qsort函数介绍

  • 函数原型
    • void qsort(void * base, size_t nitems, size_t size, int ( * compar)(const void * , const void * ))
    • 对base所指的数组进行排序。
  • 参数解释
    • base: 指向要排序的数组的第一个元素的指针
    • nitems: 由 base 指向的数组中元素的个数
    • size: 数组中每个元素的大小,以字节为单位。
    • compar: 用来比较两个元素的函数,即函数指针(回调函数)
  • qsort函数(全称quicksort)是ANSI C标准中提供的,声明在stdlib.h文件中,是根据二分法写的,其时间复杂度为n*log(n)。
  • 举一个栗子
qsort(array,n,sizeof(int),comp);

cmp比较函数

  • cmp函数的介绍
    • compar参数指向一个比较两个元素的函数。比较函数的原型应该像下面这样。注意两个形参必须是const void* 型,同时在调用compar 函数(compar实质为函数指针,这里称它所指向的函数也为compar)时,传入的实参也必须转换成const void* 型。在compar函数内部会将const void *型转换成实际类型。
    • 该函数不返回任何值。
  • 函数原型
    • int compar(const void *p1, const void *p2);
    • 默认升序排列(从小到大),如果想降序排列返回 * b- * a即可
  • 返回值解释
    • 如果compar返回值小于0(<0),那么p1所指向元素会被排在p2所指向元素的左面
    • 如果compar返回值等于0(=0),那么p1所指向元素与p2所指向元素的顺序不确定
    • 如果compar返回值大于0(>0),那么p1所指向元素会被排在p2所指向元素的右面

cmp不同的应用场景

一维数组

  • 下面是由小到大排序,return *(int *)b - *(int *)a; 为由大到小排序。
  • 参数列表是两个空指针,现在他要去指向你的数组元素。所以转换为你当前的类型,然后取值。
  • 默认升序排列(从小到大),如果想降序排列返回 * b- * a即可。

对int和char类型的排序
  • return * (int*)a - * (int*)b ;
  • return * (char*)a - * (char*)b ;
  • 程序如下
//写法一:
int cmp (const void * a, const void * b)
{
   return  *(int*)a - *(int*)b ;
   //return  * (char*)a - * (char*)b ;
}

//写法二:
int cmp(const void* _a , const void* _b)  //参数格式固定
{
 	int* a = (int*)_a;    //强制类型转换
    int* b = (int*)_b;
    // 	int* a = (char*)_a;    //强制类型转换
    //int* b = (char*)_b;
    return *a - *b;  
}

对double类型数组的排序
  • return * (double*)a>* (double*)b ? 1 : -1;
  • 在对浮点或者double型的一定要用三目运算符,因为要是使用像整型那样相减的话,如果是两个很接近的数则可能返回一个很小的小数(大于-1,小于1),而cmp的返回值是int型,因此会将这个小数返回0,系统认为是相等,失去了本来存在的大小关系。
  • 程序如下:
double in[100];
int cmp_double(const void* _a , const void* _b)  //参数格式固定
{
    double* a = (double*)_a;    //强制类型转换
    double* b = (double*)_b;
    return *a > *b ? 1 : -1;   //特别注意
}


对字符串进行排序
  • 程序如下:
char word[100][10];
int cmp_string(const void* _a , const void* _b)  //参数格式固定
{
    char* a = (char*)_a;  //强制类型转换
    char* b = (char*)_b;
    return strcmp(a,b);
}
qsort(word,100,sizeof(word[0]),cmp_string); 

  • 接着在主函数中使用qsort(nums,numsSize,sizeof(int),cmp); 进行升序排序操作。

二维数组

  • 输入一个二维数组:int a[1000][2];
  • 其中按照a[0]的大小进行一个整体的排序,其中a[1]必须和a[0]一起移动交换。(即第一行和第二行(a[0]和a[1]分别代表第一行和第二行的首地址)。)
  • 使用库函数排序的代码量并不比用冒泡排序法小,但速度却快很多。
  • 因为是二维数组,所以在一个维度相等的时候,比较另一个维度的大小。即下面程序中的:
    • else return ((int * )a)[1] - ((int * )b)[1];
qsort(a,1000,sizeof(int)*2,comp);
int comp(const void*a,const void*b)
{
return((int*)a)[0]-((int*)b)[0];
}

  • 函数指针定义(对于非malloc申请的多维数组):
int cmp(const void *a, const void *b) {
    if (((int *)a)[0] != ((int *)b)[0])
        return ((int *)a)[0] - ((int *)b)[0];
    else return ((int *)a)[1] - ((int *)b)[1];
}

  • 函数指针定义(对于malloc申请的多维数组):
int cmp(const void *a, const void *b)
{
    int *_a = *(int **)a;
    int *_b = *(int **)b;
    if (_a[0] != _b[0]) return _a[0] - _b[0];
    else return _a[1] - _b[1];
}

  • 仅对二维数组某行排序
int b_arr[][3] = { {3, 2, 5},
                   {1, 1, 8},
                   {6, 2, 9},
                   {9, 2, 4} };
//仅对第1行排序,修改如下:
//cmp2中  
((int *)a)[0] - ((int *)b)[0];  //直接比较当前传入的二维数组所对应的值
//qsort中
 qsort(b_arr, 3, 1 * sizeof(int), cmp2);  
//等价于  
qsort(&(b_arr[0][0]), 3, 1 * sizeof(int), cmp2);
//传入第1行第1列首地址,只比较3个元素,元素相关空间只有1int大小即4byte。


//仅对第2行排序,修改如下:
//cmp2中  
 ((int *)a)[0] - ((int *)b)[0]
//qsort中  
qsort(&b_arr[1][0], 3, 1 * sizeof(int), cmp2);
//传入第2行第1列首地址,只比较3个元素,元素相关空间只有1int大小即4byte。所以只比较第2行开始的3个int数据。


//仅对第3行排序,修改如下:
//cmp2中
((int *)a)[0] - ((int *)b)[0]
//qsort中,
qsort(&b_arr[2][0], 3, 1 * sizeof(int), cmp2);


  • 按某列大小来排各行的序:
  • 按某列的元素大小来排各行的序,内部连续存储,qsort实现只是根据某列元素比较结果,将该行元素从行首开始批量挪固定长度的数据交换。
  • 利用快排的入参中,修改比较函数,由于二维数组存储是连续的,所以可以仅比较某列的元素来实现。
//按第1列的元素大小排序,将各行调整为1列的大小升序排列,那需要整体移动一行的数据,大小即3 * sizeof(int)。
//qsort函数一直为:
qsort(b_arr, 3, 3* sizeof(int), cmp2);
//cmp2中
 ((int *)a)[0] - ((int *)b)[0]  
//a为某一行的首地址,向后偏移0位,用[0]还有解引用的功能,即取a[i][0]

//按第2列的元素大小排序
((int *)a)[1] - ((int *)b)[1]
//a为某一行的首地址,向后偏移1位,即取a[i][1]

//按第3列的元素大小排序
((int *)a)[2] - ((int *)b)[2]

  • 按某行大小来排各列的序
    • 如果需要按某行的元素来排各列的序,因为在内部存储时不是连续的,最直接的方式是将数组行列互换然后再排序,之后再转回来

  • 最后在主程序中使用qsort(nums,numsSize, sieof(int *), cmp);

结构体

  • 很多时候我们都会对结构体排序,一般这个时候都在cmp函数里面先强制转换了类型,不要在return里面转换,这样程序会更清晰。
对结构体中某个关键字排序(对结构体一级排序):
//写法一:
 struct node 
 { 
     double data; 
     int no; 
 } s[100];
 int i,n;
 int cmp(const void *a,const void *b) 
 { 
      struct node *aa=(node *)a; 
      struct node *bb=(node *)b; 
      return(((aa->data)>(bb->data))?1:-1); 
 }
 
//写法二:
structNode
{
	double data;
	int other;
}s[100];
int Comp(constvoid*p1,constvoid*p2)
{
	return(*(Node*)p2).data>(*(Node*)p1).data?1:-1;
}
qsort(s,100,sizeof(s[0]),Comp);

按结构体中多个关键字排序(对结构体多级排序)[以二级为例]:
//写法一:
struct node 
  { 
       double data; 
       int no; 
  } s[100];
  
 int i,n;
 int cmp(const void *a,const void *b) 
 { 
      struct node *aa=(node *)a; 
      struct node *bb=(node *)b; 
      if(aa->data!=bb->data) 
          return(((aa->data)>(bb->data))?1:-1); 
      else 
          return((aa->no)-(bb->no)); 
 }
 
//写法二:
struct Node
{
	int x;
	int y;
}s[100];
//按照x从小到大排序,当x相等时按y从大到小排序
int Comp(const void*p1,const void*p2)
{
	struct Node*c=(Node*)p1;
	struct Node*d=(Node*)p2;
	if(c->x!=d->x)	return c->x - d->x;
	else return d->y - c->y;
}

对结构体中字符串进行排序:
//写法一:
int cmp(const void *a,const void *b) 
  { 
       struct node *aa=(node *)a; 
       struct node *bb=(node *)b; 
       if(aa->data!=bb->data) 
           return(((aa->data)>(bb->data))?1:-1); 
       else 
           return((aa->no)-(bb->no));
       else 
          return strcmp(aa.str,bb.str);
          //return strcmp(aa->str,bb->str);
          //按照结构体中字符串str的字典顺序排序
 }
 
//写法二:
struct Node
{
	int data;
	char str[100];
}s[100];
//按照结构体中字符串str的字典序排序
int Comp(const void*p1,const void*p2)
{
	return strcmp((*(Node*)p1).str,(*(Node*)p2).str);
}
qsort(s,100,sizeof(s[0]),Comp);

例程

//一维数组类型
#include <stdio.h>
#include<stdlib.h>
int jobs[] = {3,2,3,7,1,2,5}, jobsSize = 7;
int cmp (const void * a, const void * b)
{
   return ( *(int*)a - *(int*)b );
}
int main(){
	 qsort(jobs, jobsSize, sizeof(int), cmp);
	 printf("%d %d %d %d %d %d %d\n",jobs[0],jobs[1],jobs[2],jobs[3],jobs[4],jobs[5],jobs[6]);
} 
//二维数组类型(对于非malloc申请的多维数组)
int cmp_intTwo(const void *a, const void *b) {
    if (((int *)a)[0] != ((int *)b)[0])
        return ((int *)a)[0] - ((int *)b)[0];
    else return ((int *)a)[1] - ((int *)b)[1];
}
int main()
{
    int arr[5][2] = {{2, 1}, {1, 2}, {2, 2}, {1, 1}, {1, 3}};
    qsort(arr, 5, sizeof(arr[0]), cmp_intTwo);
    for (int i = 0; i < 5; ++i) {
        for (int j = 0; j < 2; ++j)
        printf("%d ", arr[i][j]);
        printf("\n");
    }
    return 0;
}
//二维数组类型(对于malloc申请的多维数组)
int cmp_intTwoExtend(const void *a, const void *b)
{
    int *_a = *(int **)a;
    int *_b = *(int **)b;
    if (_a[0] != _b[0]) return _a[0] - _b[0];
    else return _a[1] - _b[1];
}
int main()
{
    int row = 3;
    int **arr1 = (int **) malloc(sizeof(int *) * row);
    for (int i = 0; i < 3; ++i) {
        arr1[i] = (int *) malloc(sizeof(int) * 2);
    }
    arr1[0][0] = 1, arr1[0][1] = 2;
    arr1[1][0] = 1, arr1[1][1] = 1;
    arr1[2][0] = 0, arr1[2][1] = 2;
    qsort(arr1, 3, sizeof(arr1[0]), cmp_intTwoExtend);
    for (int i = 0; i < 3; ++i) {
        for (int j = 0; j < 2; ++j)
        printf("%d ", arr1[i][j]);
        printf("\n");
    }
    for (int i = 0; i < 3; ++i)
         free(arr1[i]);
    free(arr1);
    return 0;
}

参考文献:

C 库函数 - qsort()
C语言qsort函数用法
C语言中qsort()库函数的排序应用
qsort百度百科
qsort用法
C学习:qsort快排函数在二维数组中的灵活应用

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值