qsort对不同数据类型的排序、及模拟qsort函数实现

本文详细介绍了C语言中的qsort函数如何对不同数据类型(整型、结构体、浮点型和字符)进行排序,包括代码展示和解析。同时,文章探讨了如何模拟实现qsort功能,强调了比较函数在排序过程中的作用,并提供了模拟qsort的冒泡排序版本。
摘要由CSDN通过智能技术生成

目录

1.qsort排序对不同数据类型的排序:

1.1 对上述五种类型的qsort排序的代码总览:

1.2 qsort对整型数据的排序:

1.2.1 代码展示:

1.2 qsort对结构体的排序:

1.2.1代码展示:

1.2.2代码解析:

1.3 qsort对于字符的排序:

1.3.1 代码展示:

1.3.2代码解析:

1.4 qsort对浮点型的排序:

1.4.1 代码展示:

 1.4.2 代码解析:

2.模拟实现qsort函数的功能 :

2.1 如何模拟实现qsort函数:

2.2模拟qsort函数的代码:

2.3 模拟qsort函数整体代码:


在上一篇文章中,介绍了冒泡排序和qsort排序,但是对qosrt排序仅仅介绍了如何应用于整型数据排序。这篇文章将介绍整型、浮点型、字符、字符串、结构体这些类型的数据在qsort排序中如何应用。以及介绍qsort整个函数是如何实现对数据进行排序的

1.qsort排序对不同数据类型的排序:

1.1 对上述五种类型的qsort排序的代码总览:

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


int compare_int(const void* p1, const void* p2)
{
	return(*(int*)p1 - *(int*)p2);
}


void print(int* arr, int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}

}

//对整型进行排序
 void test1() 
{
	int arr[10] = { 3,6,8,7,5,9,4,1,2,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), compare_int);
	print(arr, sz);
}
 //对结构体进行排序
 struct stu
 {
	 char name[10];
	 int age;
 };
 //对结构体中的年龄进行排序
 int compare_stu_age(const void* p3, const void* p4)
 {
	 return((struct stu*)p3)->age - ((struct stu*)p4)->age;
 }
 //对结构体中的名字进行排序
 int compare_stu_name(const void* p5, const void* p6)
 {
	 return strcmp(((struct stu*)p5)->name,((struct stu*)p6)->name);
 }
 void test2()
 {
	 struct stu arr1[] = { {"zhangsan",26},{"lisi",20},{"wangwu",18},{"zhaoliu",24} };
	 int sz1 = sizeof(arr1) / sizeof(arr1[0]);
	 qsort(arr1, sz1, sizeof(arr1[0]), compare_stu_age);
	 qsort(arr1,sz1,sizeof(arr1[0]), compare_stu_name);
 }
 //对字符进行排序
 int compare_char(const void* p7, const void* p8)
 {
	 return strcmp((char*)p7, (char*)p8);
 }


 void test3()
 {
	 char arr3[20] = { 'b','f','a','e','c' };
	 int sz3 = sizeof(arr3) / sizeof(arr3[0]);
	 qsort(arr3, sz3, sizeof(arr3[0]), compare_char);
 }

 //对浮点型进行排序
 int compare_float(const void* p9, const void* p10)
 {
	 return (*(float*)p9 - *(float*)p10);
 }
 void print(float* arr4, int sz4)
 {
	 int i = 0;
	 for (i = 0; i < sz4; i++)
	 {
		 printf("%f ", arr4[i]);
	 }
 }
 

 void test4()
 {
	 float arr4[4] = { 2.1,1.2,1.6,4.6 };
	 int sz4 = sizeof(arr4) / sizeof(arr4[0]);
	 qsort(arr4, sz4, sizeof(arr4[0]), compare_float);
	 print(arr4, sz4);
 }
int main()
{
	test1();
	test2();
	test3();
	test4();
	return 0;
}

1.2 qsort对整型数据的排序:

1.2.1 代码展示:

int compare_int(const void* p1, const void* p2)
{
	return(*(int*)p1 - *(int*)p2);
}


void print(int* arr, int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}

}

//对整型进行排序
 void test1() 
{
	int arr[10] = { 3,6,8,7,5,9,4,1,2,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), compare_int);
	print(arr, sz);
}
int main()
{
	test1();//test1函数对应整型排序
	test2();
	test3();
	test4();
	return 0;
}

 由于上篇文章中已经说明如何实现qsort对整型数据的排序,这里不再说明。

(文章链接:C语言——冒泡排序和qsort排序_爱写代码的粉毛护理的博客-CSDN博客

1.2 qsort对结构体的排序:

1.2.1代码展示:

 //对结构体进行排序
 struct stu
 {
	 char name[10];
	 int age;
 };
 //对结构体中的年龄进行排序
 int compare_stu_age(const void* p3, const void* p4)
 {
	 return((struct stu*)p3)->age - ((struct stu*)p4)->age;
 }
 //对结构体中的名字进行排序
 int compare_stu_name(const void* p5, const void* p6)
 {
	 return strcmp(((struct stu*)p5)->name,((struct stu*)p6)->name);
 }
 void test2()
 {
	 struct stu arr1[] = { {"zhangsan",26},{"lisi",20},{"wangwu",18},{"zhaoliu",24} };
	 int sz1 = sizeof(arr1) / sizeof(arr1[0]);
	 qsort(arr1, sz1, sizeof(arr1[0]), compare_stu_age);
	 qsort(arr1,sz1,sizeof(arr1[0]), compare_stu_name);
 }
int main()
{
	test1();
	test2();//对应结构体排序
	test3();
	test4();
	return 0;
}

1.2.2代码解析:

首先创造结构体,下面的例子中,结构体包含‘姓名’和‘年龄’分别对应了字符串类型和整型类型,这里着重去解释对字符串类型的排序

 struct stu
 {
	 char name[10];
	 int age;
 };
void test2()
 {
 	 struct stu arr1[] = { {"zhangsan",26},{"lisi",20},{"wangwu",18},{"zhaoliu",24} };
 }

上篇文章中提到,对于qsort排序,其参数一共有四个,分别是:

参数void*basesize_t  numsize_t size比较函数
被排序的数组的地址被排序的数组的元素个数被排序数组的元素大小需要自己编写

因此,便可以得到以下代码:

qsort(arr1,sz1,sizeof(arr1[0]), compare_stu_name);

 接着再去编写用于比较的函数,这里将用于比较的函数命名为:compare_stu_name

int compare_stu_name(const void* p5, const void* p6)
 {
	 return strcmp(((struct stu*)p5)->name,((struct stu*)p6)->name);
 }

对于sqort函数的返回值:

这里对名字的排序,由于姓名是字符串,所以需要用到字符串比较函数strcmp:

其中strcmp函数的两个参数分别是用于比较的字符串str1和str2,其格式为:

strcmp(const char *s1,const char *s2);

 因为在定义一个字符串时,通常是通过定义一个字符数组,然后再向字符数组中填写字符串。而在前面对于指针介绍的文章中多次提到一句话,即:数组名=首元素地址,所以,再定义了字符数组str1和str2后,在strcmp函数中填入这两个字符数组的数组名即可。

上述代码中,在结构体中填入了四个人名,这四个人名的首字符分别是:z l w z,所以,进行排序后,排序的结果应该是按照这些字母的大小来进行升序排序。结果如下:

可以看到,结果满足升序顺序。 

1.3 qsort对于字符的排序:

1.3.1 代码展示:

//对字符进行排序
 int compare_char(const void* p7, const void* p8)
 {
	 return strcmp((char*)p7, (char*)p8);
 }


 void test3()
 {
	 char arr3[20] = { 'b','f','a','e','c' };
	 int sz3 = sizeof(arr3) / sizeof(arr3[0]);
	 qsort(arr3, sz3, sizeof(arr3[0]), compare_char);
 }
int main()
{
	test1();
	test2();
	test3();//对应字符排序
	test4();
	return 0;
}

1.3.2代码解析:

对于如何排序字符,其实和排序字符串是一样的,同样是用strcmp函数。因为单个字符可以看作长度为1的字符串。因此不做解释,只给出结果:

可以看到,排序时按照字母的大小升序排序的。

1.4 qsort对浮点型的排序:

1.4.1 代码展示:

//对浮点型进行排序
 int compare_float(const void* p9, const void* p10)
 {
	 return (*(float*)p9 - *(float*)p10);
 }
 void print(float* arr4, int sz4)
 {
	 int i = 0;
	 for (i = 0; i < sz4; i++)
	 {
		 printf("%f ", arr4[i]);
	 }
 }
 

 void test4()
 {
	 float arr4[4] = { 2.1,1.2,1.6,4.6 };
	 int sz4 = sizeof(arr4) / sizeof(arr4[0]);
	 qsort(arr4, sz4, sizeof(arr4[0]), compare_float);
	 print(arr4, sz4);
 }
int main()
{
	test1();
	test2();
	test3();
	test4();
	return 0;
}

 1.4.2 代码解析:

浮点型和整型的排序大致相同,只是需要将数据类型从int改为float.上述代码顺便编写了打印函数对排序后的小数进行打印,结果如下:

满足对数据的升序排序。

2.模拟实现qsort函数的功能 :

2.1 如何模拟实现qsort函数:

上篇文章在说明为什么要引入qsort函数的时候提到过,相比于冒泡排序,qsort函数的特点有:

1.qosrt排序是快速排序

2.qsort适用于任意类型的数据的排序

不过,在进行qosrt排序时,其默认的排序顺序是升序,所以,便可以通过对冒泡排序改进来模拟实验qsort排序。

对于冒泡排序,他的第一个不足便是不能向qsort排序一样,适用于任意类型的数据,所以,在传递参数时,将传递的类型改为void*即可。

下面,给出上篇文章中冒泡排序的代码:


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

————————————————
版权声明:本文为CSDN博主「爱写代码的粉毛护理」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/2301_76836325/article/details/131625008

 可以看到,若将冒泡排序改为qsort排序,其代码中的排序趟数和一趟排序中元素两两对比的次数是不需要改变的,需要改变的,仅仅是对于不同的数据类型如何进行比较,例如对于整型只需要使用> < =即可实现,但是对于字符串类型,则需要strcmp函数。

为了解决这个问题,可以对于不同的数据类型编写不同的比较函数,并且把这些比较函数作为参数传到冒泡排序的函数参数中,这就是为什么sqort函数的参数中,存在着一个比较函数。

在冒泡排序中,传入的参数有数组地址(数组名),数组元素个数,但是在qsort排序中,却还多了一个数组中元素的大小,这是因为,qsort排序进行传参时,为了满足多个数据类型这个目的,所以指针类型是void*,但是,在上篇文章中说过,void*类型的数据,无法进行加减整数,也无法判断这个类型的指针访问内容的权限是几个字节。所以,想要对不同的元素进行比较,不但要有冒泡排序中的两个参数,还需要被排序数组的元素大小。这也就是qsort函数的参数为什么是四个的原因。

2.2模拟qsort函数的代码:

通过上面的分析,可以初步将冒泡排序改成下面的代码:

void bubble_qsort( void*base,int num,int size,int(*comp)(const void*,const void*)
{
}

int compare(const void* p1, const void* p2)
{
	return(*(int*)p1 - *(int*)p2);
}

int main()
{
	int arr[20] = { 3,2,5,6,9,4,1,8,7,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_qsort(arr, sz, sizeof(arr[0]), compare);
}

其中,bubble_qsort是模拟的qsort函数。且传递的四个参数满足qsort函数的要求。即数组地址,数组元素个数,数组元素大小,用函数指针来指定的比较函数compare。下一步,便是对bubble_qsort函数内部的编写,即:

void bubble_qsort( void*base,int num,int size,int(*comp)(const void*,const void*))
{
	int i = 0;
	for (i = 0; i < num; i++)
	{
		int j = 0;
		for (j = 0; j < num - i - 1; j++);
		if (compare((char*)base + size * j, (char*)base + (j + 1) * size)>0)
		{
			swap((char*)base + size * j, (char*)base + (j + 1) * size,num);
		}
	}
}

对于上述代码,和冒泡排序不一样的就在于第二个for循环之后,if语句的内容中,因为上面说到,qsort函数为了满足对不同的数据类型进行排序,所以选择void*型指针,但是对于不同的元素类型,他们所占的空间(或者说他们自身的大小)是不一样的,这就导致了,不同元素的情况下,一个元素和他们后面的一个元素,相隔的空间大小是不同的。所以,这里在强制转换类型时,选择转换为char*类型。这是因为char*类型的元素所占空间的大小仅为一个字节,所以,针对于不同的数据类型,可以通过char*类型所占空间为1字节大小的特点,自由的切换跳过空间的大小,并且这个跳过的值由size*j进行表示(例如 int类型,他的size = 4,所以跳过的空间的大小就是 4)

对于其中的compare函数,则是:

int compare(const void* p1, const void* p2)
{
	return(*(int*)p1 - *(int*)p2);
}

这也恰好应对了上面,为什么if语句中的判断条件最后有一个>0,因为qsort的排序默认是升序的并且返回值是由 >0, <0, =0三种类型,所以必须在If语句中设置,只有返回值>0,即对应了数组中前一个元素大于后一个元素。才可以进行交换。

对于if循环中的swap,即交换函数,其代码如下:

void swap(char* s1, char* s2,int num1)
{
	int i = 0;
	for (i = 0; i < num1; i++)
	{
		int tmp = *s1;
		*s1 = *s2;
		*s2 = tmp;
		s1++;
		s2++;
	}
}

本质和交换两个变量中的内容思路一样,通过创建一个变量tmp来辅助完成交换,不同的是,这里的交换是需要对数组中的每个元素进行交换的,所以在每一次交换完成后,必须移动到下一个数组中的内容,即对应了s1++,s2++。

2.3 模拟qsort函数整体代码:

总体代码如下:

void swap(char* s1, char* s2,int num1)
{
	int i = 0;
	for (i = 0; i < num1; i++)
	{
		int tmp = *s1;
		*s1 = *s2;
		*s2 = tmp;
		s1++;
		s2++;
	}
}

void bubble_qsort( void*base,int num,int size,int(*comp)(const void*,const void*))
{
	int i = 0;
	for (i = 0; i < num; i++)
	{
		int j = 0;
		for (j = 0; j < num - i - 1; j++);
		if (compare((char*)base + size * j, (char*)base + (j + 1) * size)>0)
		{
			swap((char*)base + size * j, (char*)base + (j + 1) * size,num);
		}
	}
}

int compare(const void* p1, const void* p2)
{
	return(*(int*)p1 - *(int*)p2);
}

int main()
{
	int arr[20] = { 3,2,5,6,9,4,1,8,7,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_qsort(arr, sz, sizeof(arr[0]), compare);
}

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

起床写代码啦!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值