qsort函数的使用和模拟实现排序

发布时间:2024年01月07日

本文介绍:

1.qsort函数的构成

2.qsort的使用

3.用qsort的实现原理模拟实现可排序所有类型数据的冒泡排序

自我介绍:一个脑子不好的大一学生,c语言接触还没到半年,若涉及到效率等问题,各位都可以在评论区提出见解,谢谢啦。

该账号介绍:此帐号会发布游戏(目前还只会简单小游戏),算法,基础知识等内容。

文章特点:会将重要步骤和易错点在代码中用注释标示(方便各位理解和定位)

1.qsort函数的构成

qsort是一个强大的函数,它可以比较任何类型的数据,整型已是so easy,它还可以比较浮点数字符,甚至是结构体,但是先别急,容我先讲讲它的构成再将其使用

由图可知,qsort函数的返回类型为int,第一个参数为void*,第二个和第三个参数为size_t,也就是unsigned int,第四个参数为函数指针,其参数为const void*,返回类型为int,在此,为了方便大家理解,我将其函数构成简化为如下部分:

void qsort(void* base,

? ? ? ? ? ? ? ? size_t num,

? ? ? ? ? ? ? ? size_t width,

? ? ? ? ? ? ? ? int(cmp)(const void*e1,const void*e2)

? ? ? ? ? ? ? ? )

构成拆分

1.void* base:起始元素的地址(传参时通常为数组名)
(1)星号:

那个星号格外耀眼是不是,因为所有要比较的东西都是类似于数组的东西啦,必须要用指针传参哦,让它知道要比较的地方是哪里

(2)void:

为什么为void呢?而不是整型?

因为qsort是一个强大的函数啦,它不能独宠一种数据,否则会引起公愤滴!

void是泛型,它可以接受任何数据类型,因此这里是void啦

?2.size_t num:要比较的元素的个数
(1)num:

这就不用过多解释啦

(2)size_t:

个数不能为负数啦

3.width:元素的大小,单位为字节
(1)作用:

因为它可以接受任何数据,因此必须传给它一个大小才方便进行比较

4.int(cmp)(const void*e1,const void*e2)
(1)int:

该函数的返回类型

(2)const void*e1,const void*e2:

该函数的两个参数的

(3)cmp:

比较函数(现在不懂别着急,后面你就知道啦),返回类型为int,参数为const void*,这里为const void*的原因与之前一样,它方便接受各种类型的数据

(4)_cdecl:

函数调用约定,这里就需要你自行了解啦,它在这里作用不大,我就不进行叙述啦

2.qsort函数的使用

(这里就主要介绍cmp比较函数的构成啦,其他部分在后续代码中就能理解啦)

cmp函数:

比较函数,我将对它分为自定义类型数据比较和自带类型分别进行介绍

我们要设计一个比较函数,先要搞清它的返回类型参数,而这里在前面的qsort函数的介绍部分就可知:

返回类型:int
参数:const void*

由此,我们可以构造出它的框架啦:

int cmp(const void*e1,const void*e2)
{

}

要对其进行比较大小,是不是要对其进行解引用呢?

那写成这样行不行?(本文默认为升序排列)

int cmp(const void*e1,const void*e2)
{
    return (*e2-*e1);
    //若为降序:return (*e1-*e2);
}

答案是:错误

原因:因为它是void*类型,void*类型不可被解引用,因此要对它进行强制类型转换

正确写法:

若为整数:

int cmp(const void*e1,const void*e2)
{
    return (*(int*)e2-*(int*)e1);
    //若为降序:return (*(int*)e1-*(int*)e2);
}

若为字符:

int cmp(const void*,const void*)
{
    return  strcmp(*(char*)e1,*(char*)e2);
}
调用函数
(1)内置类型

下面我用整型来举例:

int main()
{
    int arr[10]={10,9,8,7,6,5,4,3,2,1};
    int sz=sizeof(arr)/sizeof(arr[0]);
    qsort(arr,sz,sizeof(arr[0]),cmp);
    //调用函数的方法
    //arr:数组名
    //sz:数组元素个数
    //sizeof(arr[0]):元素大小
    //cmp:比较函数
    	return 0;
}
(2)结构体

以下为结构体的调用:

struct STU
{
    char name[20]={0};
    int age;
};
//注意有分号哦

int cmp(const void*,const void*)
{
       return ((struct STU*)e1->age - (struct STU*)e2->age);
        //与其他原理相同,要转化为对应的类型
}

int main()
{
    struct STU s[]={{“zhangsan”,15},{“lisi”,30},{“wangwu”,25}};
    //注意结构体变量的定义方法

    int sz=sizeof(s)/sizeof(s[0]);

    qsort(s,sz,sizeof(s[0]/**/),cmp);
    //调用函数的方法

	return 0;
}

以上框架还不可完全实现排序操作,下面我来用qsort函数的构成原理来写一个冒泡排序吧

3.用qsort函数的构成原理构成冒泡排序

(1)主函数部分(仍以整型举例)
int main()
{
	int arr[10] = { 10,9,8,7,6,5,4,3,2,1 };
	qsort_mao_pao_pai_xu(arr,sizeof(arr)/sizeof(arr[0]),sizeof(arr[0]),cmp);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
	return 0;
}
(2)冒泡排序构成部分
void qsort_mao_pao_pai_xu(void* base,int sz,int width, int (*cmp)(const void*, const void*))
{
	for (int i = 0; i < sz-1; i++)
	{
		int flag = 0;
        //标记部分
		for (int j = 0; j < sz - 1 - i; j++)
		{
			/*if ((*cmp)(base[j], base[j+1]))*/
			//错误示范:因为cmp要的是地址


			/*if ((*cmp)(base+j, base+j+1))*/
			//错误示范:因为base是void*,要强制类型转换

			if ((*cmp)(/**/(char*)base + j * width/**/, (char*)base + (j + 1) * width)>0)
			{
               //调用比较函数
             /*char*的原因:因为可能不一定时int类型的数字转化,char的幅度小,更灵活*/
				flag = 1;
				Swap((char*)base + j * width/**/, (char*)base + (j + 1) * width, width/*因为要知道调换几个字节*/);
			}
		}
		if (flag == 0)
        //如果内循环进行完没有,说明后续已经是升序,可以直接跳出外循环,避免无效循环,提高效率
			break;
	}
}
(3)调换数字顺序部分:
void Swap(char* e1, char* e2,int width/*因为要知道调换几个字节*/)
{
    //因为传参时已被强制类型转换为char*,所以参数直接设置为char*就可以
	for (int i = 0; i < width; i++)
	{
        //一个字节一个字节的调换,更灵活
		int t = *e1;
		*e1 = *e2;
		*e2 = t;
		e1++;
		e2++;/**/
	}
}

好啦,还有其他三种排序算法在我前面的文章已写过啦,大家可以去看看哦

链接:三大主要排序方法总结:快速排序,选择排序,冒泡排序-CSDN博客

文章来源:https://blog.csdn.net/2301_79293429/article/details/135416691
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。