详解qsort函数的使用及模拟实现qsort函数

目录

引言:

1. qsort函数简介:

qsort函数原型:

 函数参数介绍:

比较函数(compar)的编写:

(补充) void*类型的指针:

2.qsort函数示例: 

2.1对int类型排序:

2.2对char类型排序: 

2.3对浮点型排序: 

2.4对结构体类型进行排序: 

3. 模拟实现qsort函数:

结语: 


引言:

qsort函数是C语言中的一个非常有用的库函数,底层使用的是快速排序的方式,用于对数据进行排序。对数据进行排序的方法有很多,例如:冒泡排序、选择排序、插入排序等,快速排序只是这众多排序方法中的一种。这个函数可以直接使用,并且可以用来排序任意类型的数据。本文将详细介绍qsort函数的使用方法,并模拟实现一个简单的qsort函数。

1. qsort函数简介:

在学习库函数的过程中,遇到陌生的函数,我们可以通过cplusplus网站来了解它。

qsort函数原型:

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

 函数参数介绍:

详解qsort函数的使用及模拟实现qsort函数_第1张图片

比较函数(compar)的编写:

比较函数是qsort函数的一个重要参数,它定义了排序的规则。比较函数应满足以下条件:

详解qsort函数的使用及模拟实现qsort函数_第2张图片

qsort函数compar函数规定了特定的参数,所以我们设计cmp函数时要严格遵守其参数设定。需要注意的是:函数的返回结果一定要确保是整型,如果不是一定要强制类型转换成整型。 

整型数据的比较 :

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

浮点型数据的比较: 

int cmp_float(const void* e1, const void* e2)
{
	return (int)(*(float*)e1 - *(float*)e2);
}

字符串大小的比较: 

int cmp_str_size(const void* e1, const void* e2)
{
	return strcmp((char*)e1, (char*)e2);
}

字符串长度的比较:

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

结构体变量的比较: 

int cmp_by_name(const void* e1, const void* e2)
{
	return (int)(((stu*)e1)->age - ((str*)e2)->age);
}

(补充) void*类型的指针:

对于int*的指针+1跳过一个整型,解引用访问4个字节char*的指针+1跳过1个字节,解引用访问1个字节。但是对于void*的指针既不能进行解引用操作,也不能进行加减整数的操作,它是一个无具体类型的指针,就像一个“垃圾桶”一样,可以用来存放任意类型数据的地址,使用的时候,只需进行强制类型转换即可。

2.qsort函数示例: 

2.1对int类型排序:

升序情况:

#include 
#include //使用qsort函数需要包含头文件

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

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

	qsort(arr, sz, sizeof(arr[0]), cmp_int);
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}

	return 0;
}

运行结果: 

详解qsort函数的使用及模拟实现qsort函数_第3张图片

降序情况: 

#include 
#include //使用qsort函数需要包含头文件

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

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

	qsort(arr, sz, sizeof(arr[0]), cmp_int);
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}

	return 0;
}

运行结果:

2.2对char类型排序: 

#include 
#include //使用qsort函数需要包含头文件

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

int main()
{
	char arr[] = { 'b','e','f','c','a','d' };
	int sz = sizeof(arr) / sizeof(arr[0]);

	qsort(arr, sz, sizeof(arr[0]), cmp_char);
	for (int i = 0; i < sz; i++)
	{
		printf("%c ", arr[i]);
	}

	return 0;
}

运行结果:

2.3对浮点型排序: 

注意:

在使用qsort函数浮点型进行排序时,需要使用三目运算符(条件运算符)来定义比较函数的返回值。这是因为浮点数存在精度问题,直接使用减法运算符来比较两个浮点数可能会导致不准确的结果。在比较函数中,返回值为负数表示第一个元素小于第二个元素,返回值为表示两个元素相等,返回值为正数表示第一个元素大于第二个元素。

#include 
#include //使用qsort函数需要包含头文件

int cmp_float(const void* e1, const void* e2)
{
	return ((*(float*)e1) > (*(float*)e2) ? 1 : -1);
}

int main()
{
	float arr[] = { 1.2, 5.6, 8.7, 3.4, 9.0 };
	int sz = sizeof(arr) / sizeof(arr[0]);

	qsort(arr, sz, sizeof(arr[0]), cmp_float);
	for (int i = 0; i < sz; i++)
	{
		printf("%.1f ", arr[i]);
	}

	return 0;
}

运行结果:

2.4对结构体类型进行排序: 


#include 
#include //使用qsort函数需要包含头文件
#include 

struct stu
{
	char name[20];
	int age;
};

int cmp_age(const void* e1, const void* e2)
{
	return ((struct stu*)e1)->age - ((struct stu*)e2)->age;
}

int main()
{
	struct stu arr[3] = { {"zhangsan", 20}, {"lisi", 18}, {"wangwu", 36} };
	int sz = sizeof(arr) / sizeof(arr[0]);

	qsort(arr, sz, sizeof(arr[0]), cmp_age);
	for (int i = 0; i < sz; i++)
	{
		printf("%s %d ", arr[i].name, arr[i].age);
	}

	return 0;
}

运行结果:

3. 模拟实现qsort函数:

我们今天模拟实现的qsort函数是在冒泡排序的基础上而实现的,那我们先来看一下冒泡排序的实现。

 冒泡排序的原理是:从左到右,相邻元素进行比较。每次比较一轮,就会找到序列中最大的一个或最小的一个,这个数就会从序列的最右边冒出来。以从小到大排序为例,第一轮比较后,所有数中最大的那个数就会浮到最右边;第二轮比较后,所有数中第二大的那个数就会浮到倒数第二个位置……就这样一轮一轮地比较,最后实现从小到大排序。

#include 

void Buttle_Sort(int arr[], int sz)
{
	//共进行sz-1趟
	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;
			}
		}
	}
}

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

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

	return 0;
}

 下面我们要做的就是改造冒泡排序,使之成为可以排序任意类型的数组的函数,以整型数组为例:

#include
void swap(char* p1, char* p2, int width) 
{
	for (int i = 0; i < width; i++) //不知道是什么类型,所以要一个字节一个字节来进行交换
	{
		char temp = *p1;
		*p1 = *p2;
		*p2 = temp;
		p1++;
		p2++;
	}
}

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


//void* base - 要求排序不同类型的数组,void*恰好能接收任意类型的数据
//int num - 元素个数
//int width - 一个元素的大小
//int (*compare)(const void* p1, const void* p2)  函数传参函数指针接收
int bubble_qsort(void* base, int num, int width,int (*compare)(const void* p1, const void* p2))) 
{
	for (int i = 0; i < num - 1; i++) 
	{
		for (int j = 0; j < num - 1 - i; j++) 
		{
			//强制类型转化为char*型的数据,是因为一开始不知道数组的类型
            //如果要访问数据的话,只能一个字节一个字节的访问
			//这样有利于排序各种类型的数   	
			if (compare((char*)base + j * width,(char*)base + (j + 1) * width) > 0) 
			{
				//写一个Swap函数来交换
				swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
			}
		}
	}


}

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

int main() 
{
	int arr[10] = { 5,7,9,1,3,4,6,8,2,0 };
	int num = sizeof(arr) / sizeof(arr[0]);//计算数组中的元素个数

    //打印排序前的数组
    printf("排序前为:");
    print(arr, num);
	printf("\n");

	bubble_qsort(arr, num, sizeof(arr[0]), compare);

	//打印排序后的数组
    printf("排序后为:");
	print(arr, num);

	return 0;
}

运行结果:

详解qsort函数的使用及模拟实现qsort函数_第4张图片

在上面的程序中,我们首先定义了一个swap函数,用于交换两个元素的值。然后,我们定义了一个bubble_qsort函数来实现冒泡排序算法。在bubble_qsort函数中,我们使用两个嵌套的循环来遍历数组,并调用compare函数比较相邻的元素。如果比较函数返回的结果大于0,说明当前元素需要交换位置,我们就调用swap函数来实现交换。通过多次遍历和交换,我们可以将数组中的元素按照指定的比较规则进行排序。在main函数中,我们定义了一个待排序的整型数组,并调用bubble_qsort函数来对其进行排序。最后,我们打印排序后的数组,输出结果为:0 1 2 3 4 5 6 7 8 9,即为排序后的数组。

结语: 

本文详细介绍了qsort函数的使用方法,并模拟实现了一个简单的qsort函数。通过使用qsort函数,我们可以方便地对各种类型的数组进行排序。如果觉得本篇文章对大家有所帮助,不妨一键三连支持一下博主,蟹蟹!

你可能感兴趣的:(C语言从入门到精通,c语言,冒泡排序,qsort函数,模拟实现qsort函数)