谈如何使用c中的qsort快速排序库函数 按主次关键字正确排序

  快排的效率很快,但是我们很少知道如何利用它进行多关键字排序,比如我想对一个数组a[i][0]进行的一个元素进行主关键字排序,又想对a[i][1]进行次关键字排序。那么接下来就是解决这个问题的方法。  

  学过数据结构的同学,应该知道快排的基本原理,就是将要排序的物品(不说成值,因为我们可能要排多维数组或者是结构体),就是每次将一个物品放到序列中最合适的位置,那么如何放,如果想要递增排序,就是前面的物品和后面的物品,谁大的放后面。所以通过这个原理就知道void qsort(void*, size_t, size_t, int*(const void*, const void*));这个函数传参数的时候为什么最后一个参数是一个函数了,真正进行排序交换的是qsort(),但是还需要一个比较两个物品的大小的操作,这个操作是需要用户自定义的,如果需要物品递增,那么这个比较函数cmp()返回的就是a>b[a-b]的true结果,这个时候排序的时候会把大的交换到后面。同理,要想物品递减,那么cmp()返回的就是a<b[b-a]了。

  现在关键的问题来了。

如果排序仅仅是一个int a[N]的数组

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

//我来解释一下为什么会 return *(int *)a-*(int *)b;这个,因为我们传进到cmp()的参数是单个物体的地址,在这里我们传的是一个存储int值的一个int类型的地址,为了保持cmp()函数的可以让任何类型的数组进行比较大小,所以再传入到cmp()里面就会被转为不可变的const 的空void类型的地址,所以当我们在这个函数内部要比较大小时,又必须将这个地址转换成存储Int类型的地址,然后去取这个地址的值进行大小比较。所以(int *)a是将这个存储空类型void的地址强转换为存储int类型的地址,而只得到这个地址是比较不了大小的,这个时候就必须比较这个存储这个地址的内容了,获得地址内容只要在地址之前加上*号就可以了即*(int*)a这个的实际意义。

同理如果你要比较结构体数组,就需要在cmp内部把const void *a单个结构体成员的地址然后就是去结构体中需要比较大小的关键字了

int cmp( const void *a ,const void *b) 
{
return (*(Node *)a).data > (*(Node *)b).data ? 1 : -1; //或者true,false
}

qsort(s,100,sizeof(s[0]),cmp);
根据这个结构体我们,同理可推出比较多维数组,这里我们用二维数组的例子,用二维数组的第二个值作为排序的关键字
int a[1000][2];
qsort(a,1000,sizeof(int)*2,comp);
 
intcomp(constvoid*a,constvoid*b)
 
{
return((int*)a)[1]-((int*)b)[1];
}
那么我们进一步思考,怎样才能是二维数组有主次关键字排序,比如我想以a[i][2]中以a[i][0]为主关键字排序,以a[i][1]为次关键字排序。
其实很简单比较一个物品大小的操作在我们手上,我们只需在cmp()最后给一个比较两个物品的大小的结构给qsort()就可以了,所以我们可以这样做:
1,如果主关键字不相等就比较主关键字的大小并返回结果。
2,如果主关键字相等,就比较次关键字的大小并返回结果就可以了。
例子:
int cmp( const void *a , const void *b )
{
if( ((int *)a)[0]==((int *)b)[0] ) return ((int *)a)[1]-((int *)b)[1];
else return ((int *)a)[0]-((int *)b)[0];
}
qsort(a[1],n,sizeof(a[1]),cmp);
这个例子还用了一个方法是如何忽略数组中a[0]的值,直接以a[1]开始排序。
那么结构体中多关键字排序就是一样的了。至此就可以利用快排达到理想的排序状态了。
 
原文地址:https://www.cnblogs.com/woshijishu3/p/3604589.html