qsort函数的模拟实现

我们知道qsort函数可以排任意类型的数据,原理是我们会给它传一个函数,通过那个函数,我们可以实现任意类型数据的排序。
qsort函数运用的是快速排序实现的,在这里我们使用冒泡排序实现一下,我们先单纯的写一个冒泡排序,

void bubble_sort(char* arr, int sz) {
	for (int i = 0; i < sz-1; i++) {
		for (int j = 0; j < sz - 1 - i; j++) {
			if (arr[j] > arr[j + 1]) {
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
}

我们会发现冒泡排序会有缺点:

缺点一:只能比较整形数据,只能传整形数组。
解决办法:使用void*的指针,(因为void*的指针可以接收任意类型的指针,但是它不能进行+1-1的操作)这样就不知道数组的个数和元素的大小,所以要传num,size

缺点二:对于不同类型的数据,不能简单使用< >==比较
解决办法:将两个元素的比较方法,以函数参数的形式传递

缺点三:不同的类型的数据,交换略有差异
解决办法:写一个交换函数,逐个字节进行交换

下面我们就来实现一下:

void Swap(char* buf1, char* buf2, int sz) {
	for (int i = 0; i < sz; i++) {
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}

void bubble_qsort(void* base, size_t num, size_t size, int(*cmp)(const void* e1, const void* e2)) {
	//趟数
	for (int i = 0; i < num-1; i++) {
		for (int j = 0; j < num - 1 - i; j++) {//每趟需要排的次数
			if ((cmp((char*)base + j * size, (char*)base + (j + 1) * size)) > 0) {//如果前边元素大于后边元素的话,就交换位置
				Swap((char*)base + j * size, (char*)base + (j + 1) * size,size);//交换函数
			}
		}
	}
}

int cmp_int(const void* e1, const void* e2) {
	return *(int*)e1 - *(int*)e2;
}
void print_arr(int* arr, int sz) {
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}
void test1() {
	int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	print_arr(arr, sz);
	bubble_qsort(arr, sz, sizeof(int), cmp_int);
	print_arr(arr, sz);
}

int main() {
	test1();//用来测试整型数组
	return 0;
}

这就是我们模拟qsort函数写的一个排序的函数,前边两个函数就相当于是一个编写qsort函数的程序员写的,后面就是用户自己写的,去使用这个函数,这个模拟实现比较难理解的是什么呢?

if ((cmp((char*)base + j * size, (char*)base + (j + 1) * size)) > 0) {//如果前边元素大于后边元素的话,就交换位置
				Swap((char*)base + j * size, (char*)base + (j + 1) * size,size);//交换函数
			}

我觉得就是这两段代码了。

因为cmp函数是用户自己写的,所以他知道要比较什么类型的数据,并且数据类型的大小也是知道的。我们在编写这个第一行时无非就是传过去两个地址嘛,别的不用管。那我们怎么找到一个不知道类型的数组中的一个元素的地址呢?
我们知道数组的起始地址,以及每个元素所占字节的大小,以及这是数组的第几个元素,但现在void*是不能±操作的,那么就强制类型转换成char*这样每次向后加一就访问一个字节,我们由知道加几个字节,这样就找到了元素的地址。

Swap函数的话是交换两个元素,我们就可以传过去每个元素的大小,然后在一个字节一个字节的交换就可以了。

这只是整形,那么能不能实现一下结构体的排序呢?

struct Stu {
	char name[20];
	int age;
};
int cmp_by_name(const void* e1, const void* e2) {
	return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
int main() {
	struct Stu arr[] = { {"zhangsan",32},{"lisi",12},{"wangwu",20}};
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_qsort(arr, sz, sizeof(arr[0]), cmp_by_name);
	for (int i = 0; i < sz; i++) {
		printf("%s %d\n", arr[i].name, arr[i].age);
	}
}

基于上面写的bubble_qsort函数我们写了这样一个代码,发现是可以排序的。
在这里插入图片描述
这是根据名字进行排序的,我们再试试根据年龄进行排序

struct Stu {
	char name[20];
	int age;
};
int cmp_by_age(const void* e1, const void* e2) {
	return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
int main() {
	struct Stu arr[] = { {"zhangsan",32},{"lisi",12},{"wangwu",20}};
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_qsort(arr, sz, sizeof(arr[0]), cmp_by_age);
	for (int i = 0; i < sz; i++) {
		printf("%s %d\n", arr[i].name, arr[i].age);
	}
}

在这里插入图片描述
肯定也是可以实现的。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值