qsort函数的使用和自实现(采用冒泡排序)

导言:

在使用C语言编写代码时,我们总会遇到一些需要我们排序的内容。如果能有一个现成帮我们写好的库函数来进行排序就能让我们的效率大大提高,事实上C语言确实也有这样的函数,只需我们调用,而本文主要就qsort函数进行讲解。

目录

正文:

一.qsort函数的使用

二.qsort函数自实现


正文:

一.qsort函数的使用

qsort()函数是C语言标准库中提供的一个排序函数,用于对数组进行排序。它的函数原型如下:

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

参数说明:

  • base:指向待排序数组的指针。
  • num:待排序数组的元素个数。
  • size:待排序数组中每个元素的大小(以字节为单位)。
  • compar:比较函数,用于确定元素之间的顺序关系。

值得注意的是函数参数base是void*类型,这也意味着无论是基本数据类型还是自定义的数据类型都可以使用该函数进行排序,size_t是一种无符号整数类型,用于表示内存大小或对象大小。而最后一个参数是一个函数指针,传参时需要将函数名当作地址传入。下面进行详细的介绍:

 compar比较函数的原型如下:

int compar(const void *a, const void *b);

参数说明:

  • a和b:指向待比较的两个元素的指针。

比较函数应返回一个整数值,表示a和b的大小关系:

  • 若返回值小于0,表示a小于b;
  • 若返回值等于0,表示a等于b;
  • 若返回值大于0,表示a大于b。

 该函数由程序员自己完成,注意用来规定比较大小的类型以及规则,可以让排序变得更加灵活。

  • 对于升序排序,比较函数应返回ab的大小关系。如果a小于b,则返回一个负数;如果a等于b,则返回0;如果a大于b,则返回一个正数。例如:
    int compare(const void *a, const void *b) {
        int num1 = *(int*)a;
        int num2 = *(int*)b;
        return num1 - num2;
    }
    

  • 对于降序排序,比较函数应返回ba的大小关系。如果b小于a,则返回一个负数;如果b等于a,则返回0;如果b大于a,则返回一个正数。
    int compare(const void *a, const void *b) {
        int num1 = *(int*)a;
        int num2 = *(int*)b;
        return num2 - num1;
    }

  • 对于自定义排序,比较函数可以根据具体需求进行编写。比较函数可以使用任何逻辑来比较ab,并返回相应的结果。

下面是qsort()函数进行排序的实例代码:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>


//自定义类型
struct Stu {
	int age;
	char name[20];
};
//基本数据类型
int cmp1(const void* p, const void* q) {
	return (*(int*)p) - (*(int*)q);
}
//自定义数据类型
//1.按年龄排序
int cmp2(const void* p, const void* q) {
	return ((struct Stu*)p)->age - ((struct Stu*)q)->age;
}
//2.按姓名排序
int cmp3(const void* p, const void* q) {
	return strcmp(((struct Stu*)p)->name, ((struct Stu*)q)->name);
}
int main() {
	//均采用升序
	int arr1[10] = { 1,5,9,6,8,7,4,2,3,10 };//基本数据数组

	struct Stu arr2[3] = { {20, "zhanhsan"},
						   {30,"lisi" },
						   {18,"wangwu"} };//自定义类型数组

	int size1 = sizeof(arr1) / sizeof(arr1[0]);//arr1的长度

	int size2 = sizeof(arr2) / sizeof(arr2[0]);//arr2的长度

	qsort(arr1, size1, sizeof(arr1[0]), cmp1);//对arr1进行排序

	qsort(arr2, size2, sizeof(arr2[0]), cmp2);//对arr2按照年龄大小排序

	//qsort(arr2, size2, sizeof(arr2[0]), cmp3);//对arr2按照姓名排序

	for (int i = 0; i < size1; i++) {
		//对arr1整形数组的排序结果
		printf("%d ", arr1[i]);
	}
	printf("\n");
	for (int i = 0; i < size2; i++) {
		//对arr2按照年龄排序的结果
		printf("%d ", arr2[i].age);
	}
	printf("\n");
	/*for (int i = 0; i < size2; i++) {
		//对arr2按照姓名排序的结果
		printf("%s ", arr2[i].name);
	}*/
	return 0;
}

运行结果如下:

由于调用时会有覆盖作用,所以这里只调用了一次arr2的年龄排序,如果调用姓名排序的话会按照字典序将姓名排好

总之,qsort()函数是一个方便、高效的排序函数,可以用于对数组进行排序。通过编写合适的比较函数,可以灵活地指定排序的顺序,满足不同的排序需求。

二.qsort函数自实现

实际上qsort函数内部采用的排序算法是快速排序,但这里为了更加简单我们采用冒泡排序实现。

实现步骤:

1.我们先对应qsort函数写出函数的原型:

void my_qsort(void* base, int num, int sz, int (*cmp)(const void* e1, const void* e2)) {

2.我们采用冒泡排序的思想来进行内部的排序,先将冒泡排序的框架打好:

void my_qsort(void* base, int num, int sz, int (*cmp)(const void* e1, const void* e2)) {
	for (int i = 0; i <num-1; i++) {
		for (int j = 0; j <num-i-1 ; j++) {
			if () {
				
			}
		}
	}
}

这里考虑到base的类型是void*,也就说我们不知道传进来的参数究竟是什么类型,自然我们也不能简单的用冒泡排序中的比较方式。我们先考虑if语句的条件,在冒泡排序中我们采用的是

if(base[j]>base[j+1]){
swap(base[j],basr[j+1]);
}

而这里由于我们并不知道数据的类型,所以无法利用这种方法。但是我们可以用指针实现操作,无论是什么类型我们先强制类型转化成char*,然后再加上对应的 下标*sz 即可实现和base[j]一样的效果。而程序员使用此函数时会自己提供一个cmp函数,我们再在这里调用,实现对两者的比较。

同理swap函数的参数也是一样。

代码如下:

void swap(char* buf1,char* buf2,int sz) {
	for (int i = 0; i < sz; i++) {
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 =tmp;
		buf1++;
		buf2++;
	}
}
if (cmp((char*)base+j*sz,(char*)base+(j+1)*sz)>0){
swap((char*)base+j*sz,(char*)base+(j+1)*sz,sz);
}

3.最后我们来实现swap函数,我们先思考冒泡排序中的处理。

void swap(int a, int b) {
	int tmp = a;
	a = b;
	b = a;
}

由于我们已经知道swap函数的两个参数都是char*类型了,这就使得交换变得简单。我们只需要知道这中类型的字节大小,再将每个字节的内容进行交换,即可实现两未知元素的交换。代码如下:

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

 最后我们来调用一下我们自己写的qsort函数试试,这里自定义数据类型我们选择调用按照姓名进行排序,代码如下:

#pragma warning(disable:4996)
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct Stu {
	int age;
	char name[20];
};
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 my_qsort(void* base, int num, int sz, int (*cmp)(const void* e1, const void* e2)) {
	for (int i = 0; i <num-1; i++) {
		for (int j = 0; j <num-i-1 ; j++) {
			if (cmp((char*)base+j*sz,(char*)base+(j+1)*sz)>0) {
				swap((char*)base+j*sz,(char*)base+(j+1)*sz,sz);
			}
		}
	}
}//基本数据类型
int my_cmp(const void* e1, const void* e2) {
	return (*(int*)e1) - (*(int*)e2);
}
//自定义数据类型
//1.按年龄排序
int cmp2(const void* p, const void* q) {
	return ((struct Stu*)p)->age - ((struct Stu*)q)->age;
}
//2.按姓名排序
int cmp3(const void* p, const void* q) {
	return strcmp(((struct Stu*)p)->name, ((struct Stu*)q)->name);
}
void print(int n,int arr[]) {
	for (int i = 0; i < n; i++) {
		printf("%d ", arr[i]);
	}
	printf("\n");
}
int main() {
	int arr[] = { 4,1,5,3,2,8,7,9,6,10 };
	my_qsort(arr, sizeof(arr)/sizeof(arr[0]), sizeof(arr[0]), my_cmp);
	print(sizeof(arr) / sizeof(arr[0]), arr);

	struct Stu arr2[3] = { {10, "张三"},
					   {20,"李四" },
					   {18,"王五"} };
	int size2 = sizeof(arr2) / sizeof(arr2[0]);
	//qsort(arr2, size2, sizeof(arr2[0]), cmp2);
	qsort(arr2, size2, sizeof(arr2[0]), cmp3);
	for (int i = 0; i < size2; i++) {
		printf("%s ", arr2[i].name);
	}
	/*for (int i = 0; i < size2; i++) {
		printf("%d ", arr2[i].age);
	}*/
	return 0;
}


运行结果如下:

可以看到排序成功了,虽然效率并不是很高,但是也能够将qsort函数的功能给还原出来了。

总结:

qsort函数在我们日常中使用的还是比较广泛的,在还没有学习很多排序算法时,qsort函数能够满足大部分的排序需求。同时,通过自实现qsort函数,我们不仅更加熟练了冒泡排序的思想和实现过程,还对qsort函数有了更深层次的理解。通过对qsort函数的自实现,我们可以更好地应对排序问题,并在实际开发中灵活运用。希望本篇文章能够对你有所帮助。

  • 0
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 22
    评论
冒泡排序和快速排序(qsort)虽然都是常见的排序算法,但它们的工作原理和效率有很大的不同。冒泡排序是一种简单的比较排序算法,而快速排序则是一种高效的分治策略。如果你想了解如何模仿`qsort`实现冒泡排序,其实这不是一个直接的对应,因为`qsort`是基于分治法的高效排序,而冒泡排序更适合教学和理解基本排序原理。 不过,为了满足你的要求,我们可以探讨一下如何用类似`qsort`的方式设计一个冒泡排序的“迭代”版本,虽然这并不是真正意义上的模仿,因为它们在实现上并不相同。 **冒泡排序的简化版(不是真正的`qsort`)**: 1. 定义一个辅助函数,类似于`qsort`的分区过程,但仅用于比较相邻元素并交换: ```c++ void bubbleSortPartition(int arr[], int low, int high) { while (low < high) { if (arr[low] > arr[high]) { std::swap(arr[low], arr[high]); } low++; high--; } } ``` 2. 用递归调用的方式实现冒泡排序: ```c++ void bubbleSortIterative(int arr[], int size) { for (int i = 0; i < size - 1; i++) { bubbleSortPartition(arr, 0, size - 1 - i); } } ``` 这里我们不是直接将整个数组作为一次排序,而是每次缩小待排序范围,直到整个序列有序。 **相关问题--:** 1. 冒泡排序与快速排序的主要区别是什么? 2. 在冒泡排序中,为什么要使用`bubbleSortPartition`函数? 3. 如何评价这种将冒泡排序与`qsort`风格结合的简化版本?
评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值