学习札记:qsort函数

在学校,虽然我们“学习了c++”,但其实只是接触了它的一点皮毛。在近期个人学习中,qsort这个在学校从未讲授过的函数进入了我的视野。

qsort函数包含在cstdlib头文件中,其作用是排序任意类型数组中的数据。它的原型如下:

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

乍一看很吓人,我们不妨拆解来看。

它不返回值,只是在默默地完成排序工作。

它的第一个参数是一个通用类型指针,说白了就是任何类型的数据都可以丢进去。

第二个参数是numbers of items,待排序元素个数。可以用sizeof(arr)/sizeof(arr[0])处理好。

第三个参数是size of item,单个元素所占空间大小,可以用sizeof(arr[0])处理。

第四个参数是一个函数指针,这个函数的两个参数是两个通用类型指针,返回一个整数。这个整数的要求是:
如果compar返回值< 0,那么p1指向元素会被排在p2指向元素的左面;
如果compar返回值= 0,那么p1指向元素与p2指向元素的顺序不确定。
如果compar返回值> 0,那么p1指向元素会被排在p2指向元素的右面。

所以,要完成不同类型数据的通用排序函数,关键功夫是在cmp函数。

int cmp_int(const void* e1, const void* e2) {//整数版本的cmp函数
	return *(int*)e1 - *(int*)e2;
}

我们可以看看整数版本的cmp函数。首先,根据qsort函数的要求,cmp函数必须返回一个整数,因此固定返回值为int。我们转回到cmp的文字定义,它要求cmp<0则e1指向e2左边去。

由此就可以选择排序方法:升序or降序?如果我们规定升序,即越小越往前排,那么e1指向e2左边就说明e1指向的值应该小于e2指向的值,也就是说符合升序的要求。此时要求cmp<0,那我们很容易想到现在e1所指向的值-e2所指向的值<0,用它做返回值再合适不过了。

然而cmp函数传参时,两个参数是(void*)类型的,是通用类型指针,不能做解引用操作。所以,为了获取我们需要的整型的数据,应该将(void*) 强制转换成(int *),然后解引用操作后,返回两个整数的差。也就是:return *(int*)e1 - *(int*)e2;这样就编写好了符合条件的cmp_int函数。

下面定义一个整形数组,填上数据,调用qsort函数对其排序,打印数组,有:

#include<iostream>
#include<cstdlib>
using namespace std;
int cmp_int(const void* e1, const void* e2) {
	return *(int*)e1 - *(int*)e2;
}
int main() {
	int arr1[] = { 5,7,8,9,1,2,0,3,4,4 };
	qsort(arr1, sizeof(arr1) / sizeof(int), sizeof(int), cmp_int);
	for (int i = 0; i < sizeof(arr1) / sizeof(int); i++) {
		cout << *(arr1 + i) << " ";
	}
	cout << endl;
}

运行结果的确是一个升序数组,为:
用qsort函数升序排列整型数组
如果把cmp函数的返回值处调换*(int*)e1 - *(int*)e2;*(int*)e2 - *(int*)e1;,就完成了从升序到降序的转变。

#include<iostream>
#include<cstdlib>
using namespace std;
int cmp_int(const void* e1, const void* e2) {
	return *(int*)e2 - *(int*)e1;
}
int main() {
	int arr1[] = { 5,7,8,9,1,2,0,3,4,4 };
	qsort(arr1, sizeof(arr1) / sizeof(int), sizeof(int), cmp_int);
	for (int i = 0; i < sizeof(arr1) / sizeof(int); i++) {
		cout << *(arr1 + i) << " ";
	}
	cout << endl;
}

用qsort函数降序排序整形数组

那么能否用它排序其他类型的数据呢?浮点型,字符串,甚至是结构体或类?

只要给出合适的cmp函数,就都可以排序!

在此给出字符串的cmp函数:

int cmp_char(const void* e1, const void* e2) {
	return *(char*)e1 - *(char*)e2;
}

因为处理字符时是按照ASCII码进行的,因此返回ASCII码的差值就可以,不用做修改。

double类型的cmp函数:

int cmp_double(const void* e1, const void* e2) {
	if (*(double*)e1 - *(double*)e2 > 0)return 1;
	else if (*(double*)e1 - *(double*)e2 < 0) return -1;
	else return 0;
}

这次返回值不能用(*(double*)e1 - *(double*)e2;了。其原因在于,原先形态的返回值其实是一个double型数据,而从double类型转变到int类型是可能丢失数据的。如果两个double型的差值在±1之内,转为int型就直接成为0了!那cmp函数的本意就被破坏掉了,因此,我们换一种返回法,只要大于0统统输出1,只要小于0统统输出-1,这样就规避了类型转换丢失数据的问题,也符合cmp函数的要求。

如果我们想要定义一个类呢?就拿三国时期的几大政权来生成一个类吧:
我们定义了一个leaders类,它有三个公共属性:国祚,国名,省份数量。在主函数中,我们用一个数组初始化了三个实例。下面,我们分别按国祚,国名,省份数量对它们进行了排序。它们的cmp函数编写与自带的数据类型的编写方法很相似。

class leaders {
public:
	int Age;
	char Name[20];
	int states;
};
int cmp_Age(const void* e1, const void* e2) {
	return ((class leaders*)e1)->Age - ((class leaders*)e2)->Age;
}
int cmp_states(const void* e1, const void* e2) {
	return ((class leaders*)e1)->states - ((class leaders*)e2)->states;
}
int cmp_Name(const void* e1, const void* e2) {
	return ((class leaders*)e1)->Name - ((class leaders*)e2)->Name;
}
int main(){
	leaders arr4[3] = { {50,"wei",7},{45,"shu",2}, {35,"wu",4} };
	qsort(arr4, sizeof(arr4) / sizeof(class leaders), sizeof(class leaders), cmp_Age);
	qsort(arr4, sizeof(arr4) / sizeof(class leaders), sizeof(class leaders), cmp_states);
	qsort(arr4, sizeof(arr4) / sizeof(class leaders), sizeof(class leaders), cmp_Name);
	for (int i = 0; i < sizeof(arr4) / sizeof(class leaders); i++) {
		cout << arr4[i].Age<< " ";
	}
	cout << endl;
	for (int i = 0; i < sizeof(arr4) / sizeof(class leaders); i++) {
		cout << arr4[i].states << " ";
	}
	cout << endl;
	for (int i = 0; i < sizeof(arr4) / sizeof(class leaders); i++) {
		cout << arr4[i].Name << " ";
	}
}

下面我把所有类型的排序一起写出,供大家参考:

#define PI 3.1416
#include<iostream>
#include<cstdlib>
using namespace std;
class leaders {
public:
	int Age;
	char Name[20];
	int states;
};
int cmp_int(const void* e1, const void* e2) {
	return *(int*)e1 - *(int*)e2;
}
int cmp_double(const void* e1, const void* e2) {
	if (*(double*)e1 - *(double*)e2 > 0)return 1;
	else if (*(double*)e1 - *(double*)e2 < 0) return -1;
	else return 0;
}
int cmp_char(const void* e1, const void* e2) {
	return *(char*)e1 - *(char*)e2;
}
int cmp_Age(const void* e1, const void* e2) {
	return ((class leaders*)e1)->Age - ((class leaders*)e2)->Age;
}
int cmp_states(const void* e1, const void* e2) {
	return ((class leaders*)e1)->states - ((class leaders*)e2)->states;
}
int cmp_Name(const void* e1, const void* e2) {
	return ((class leaders*)e1)->Name - ((class leaders*)e2)->Name;
}
int main() {
	int arr1[] = { 5,7,8,9,1,2,0,3,4,4 };
	char arr2[] = "ajhhcjhwcwauc";
	double arr3[] = { 1.5,2.3,410.7,14.015,PI };
	leaders arr4[3] = { {50,"wei",7},{45,"shu",2}, {35,"wu",4} };
	qsort(arr1, sizeof(arr1) / sizeof(int), sizeof(int), cmp_int);
	qsort(arr2, sizeof(arr2) / sizeof(char), sizeof(char), cmp_char);
	qsort(arr3, sizeof(arr3) / sizeof(double), sizeof(double), cmp_double);
	qsort(arr4, sizeof(arr4) / sizeof(class leaders), sizeof(class leaders), cmp_Age);
	qsort(arr4, sizeof(arr4) / sizeof(class leaders), sizeof(class leaders), cmp_states);
	qsort(arr4, sizeof(arr4) / sizeof(class leaders), sizeof(class leaders), cmp_Name);
	for (int i = 0; i < sizeof(arr1) / sizeof(int); i++) {
		cout << *(arr1 + i) << " ";
	}
	cout << endl;
	for (int i = 0; i < sizeof(arr2) / sizeof(char); i++) {
		cout << *(arr2 + i) << " ";
	}
	cout << endl;
	for (int i = 0; i < sizeof(arr3) / sizeof(double); i++) {
		cout << *(arr3 + i) << " ";
	}
	cout << endl;
	for (int i = 0; i < sizeof(arr4) / sizeof(class leaders); i++) {
		cout << arr4[i].Age<< " ";
	}
	cout << endl;
	for (int i = 0; i < sizeof(arr4) / sizeof(class leaders); i++) {
		cout << arr4[i].states << " ";
	}
	cout << endl;
	for (int i = 0; i < sizeof(arr4) / sizeof(class leaders); i++) {
		cout << arr4[i].Name << " ";
	}
}

结果如图,qsort函数很好地帮助我们完成了对各个类型数据数组的升序排序。如果反转return的表达式 ,就可以完成降序排列了。
结果图示

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值