C语言快速排序——qsort函数及其模拟实现

举报
未见花闻 发表于 2022/04/29 23:09:50 2022/04/29
【摘要】 C语言快速排序——qsort函数及其模拟实现

⭐️前面的话⭐️

大家好!对于排序有许多中方法,比如冒泡排序,选择排序,希尔排序,插入排序,堆排序等等,但是怎样能够使用一个函数能够对多个数据类型进行排序呢?无所不知的C语言开发者提供了一个qsort函数,它能够对多种数据类型进行排序,实现各种数据类型的快速排序,这篇文章介绍qsort函数的使用及其模拟qsort函数的实现(基于冒泡排序)。

📒博客主页:未见花闻的博客主页
🎉欢迎关注🔎点赞👍收藏⭐️留言📝
📌本文由未见花闻原创!
📆华为云首发时间:🌴2022年4月29日🌴
✉️坚持和努力一定能换来诗与远方!
💭参考书籍:📚《C语言程序设计》
💬参考在线编程网站:🌐牛客网🌐力扣
博主的码云gitee,平常博主写的程序代码都在里面。
博主的github,平常博主写的程序代码都在里面。
🍭作者水平很有限,如果发现错误,一定要及时告知作者哦!感谢感谢!


⭐️qsort库函数

🍒函数声明

void qsort( void *base, size_t num, size_t width, int (__cdecl *compare )(const void *elem1, const void *elem2 ) );

🍒库

<stdlib.h>

🍒函数功能

实现多个数据类型的快速排序。

🍒qosort函数各参数介绍

  • [ ] 返回类型:void 无返回值
  • [ ] 参数base:void* 无类型指针,可以接受任何指针类型,但是不能进行解引用,不能进行运算。该参数用来接受需要排序数组的首地址。
  • [ ] 参数num:size_t类型,本质上为无符号整型类型,该参数接受数组中元素的数量。
  • [ ] 参数width:size_t类型,本质上无符号整型类型,该参数接受数组中元素的内存大小,单位为字节。
  • [ ] 参数compare:函数指针类型,指向的函数类型为返回值为int,拥有2个参数的函数,其实这两个参数类型都为const void指针。该函数用来比较两个地址对应的元素的大小。该参数用来回调compare所指向的函数,是实现多数据类型排序的核心。

🍒compare函数

qsort函数中,其中一个参数是函数指针,指向一个用来比较两个元素大小的函数,不妨记该函数的第一个参数为e1,第二个参数为e2,两个参数均为只可读无类型指针,该函数返回值类型为int,记为x

  • [ ] x>0,表示e1指向的元素大于e2指向的元素。
  • [ ] x=0,表示e1指向的元素等于e2指向的元素。
  • [ ] x<0,表示e1指向的元素小于e2指向的元素。

实现整型类型比较的compare函数
如果qsort需要实现实现升序:

//升序
int compare(const void* a, const void* b)
{
	return (*(int*)a - *(int*)b);
}

那需要实现降序呢?
其实很简单,将上面return (*(int*)a - *(int*)b);改为return (*(int*)b - *(int*)a);就可以了。
实现浮点数比较的compare函数

int compare_dou(const void* a, const void* b)
{
	double com = (*(double*)a - *(double*)b);
	//防止数据发生截断造成排序结果错误
	if (com > 0)
		return 1;
	else if (com < 0)
		return -1;
	else
		return 0;
}
int compare_fla(const void* a, const void* b)
{
	float com = (*(float*)a - *(float*)b);
	//防止数据发生截断造成排序结果错误
	if (com > 0)
		return 1;
	else if (com < 0)
		return -1;
	else
		return 0;
}

实现字符类型和字符串比较的compare函数

int compare(const void* a, const void* b)
{
	return (*(char*)a - *(char*)b);
}

实现结构体类型比较compare函数

//参考结构体
struct stu
{
	char name[20];
	int age;
	double score;
};
int compare_stu(const void* a, const void* b)
{
	//如果是字符串
	return strcmp(((struct stu*)a)->name, ((struct stu*)b)->name);
	//如果整型
	//return (((struct stu*)a)->age - ((struct stu*)b)->age);
	//如果是浮点型
	//float com = (*(float*)a - *(float*)b);
	//防止数据发生截断造成排序结果错误
	//if (com > 0)
	//	return 1;
	/*else if (com < 0)
		return -1;
	else
		return 0;*/
}

🍒测试

测试主函数

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main ()
{
//test;
}

测试1:整数排序

void test1()
{
	int arr[] = { 2,8,6,12,3,86,1,42,66,22,98,88 };
	sz_t number = sizeof(arr) / sizeof(arr[0]);
	sz_t size = sizeof(int);
	sz_t i = 0;
	printf("数组排序前:\n");
	for (i = 0; i < number; i++)
	{
		printf("%d ", arr[i]);
	}
	qsort(arr, number, size, compare);
	printf("\n数组排序后:\n");
	for (i = 0; i < number; i++)
	{
		printf("%d ", arr[i]);
	}
}
数组排序前:
2 8 6 12 3 86 1 42 66 22 98 88
数组排序后:
1 2 3 6 8 12 22 42 66 86 88 98
D:\gtee\C-learning-code-and-project\练习使用qsort\Debug\练习使用qsort.exe (进程 37064)已退出,代码为 0。
按任意键关闭此窗口. . .

测试2:双精度浮点数排序

void test2()
{
	double arr[5] = { 3.5,8.9,9.2,4.8,2.1 };
	sz_t number = sizeof(arr) / sizeof(arr[0]);
	sz_t size = sizeof(double);
	sz_t i = 0;
	printf("数组排序前:\n");
	for (i = 0; i < number; i++)
	{
		printf("%.2lf ", arr[i]);
	}
	qsort(arr, number, size, compare_dou);
	printf("\n数组排序后:\n");
	for (i = 0; i < number; i++)
	{
		printf("%.2lf ", arr[i]);
	}
}
数组排序前:
3.50 8.90 9.20 4.80 2.10
数组排序后:
2.10 3.50 4.80 8.90 9.20
D:\gtee\C-learning-code-and-project\练习使用qsort\Debug\练习使用qsort.exe (进程 9984)已退出,代码为 0。
按任意键关闭此窗口. . .

测试3:结构体中字符串排序测试

void test3()
{
	struct stu arr[3] = { {"zhangsan", 15}, {"lisi", 30},{"wangwu", 10} };
	sz_t number = sizeof(arr) / sizeof(arr[0]);
	sz_t size = sizeof(arr[0]);
	sz_t i = 0;
	printf("数组排序前:\n");
	for (i = 0; i < number; i++)
	{
		printf("%s ", arr[i].name);
	}
	qsort(arr, number, size, compare_stu);
	printf("\n数组排序后:\n");
	for (i = 0; i < number; i++)
	{
		printf("%s ", arr[i].name);
	}
}
数组排序前:
zhangsan lisi wangwu
数组排序后:
lisi wangwu zhangsan
D:\gtee\C-learning-code-and-project\练习使用qsort\Debug\练习使用qsort.exe (进程 29572)已退出,代码为 0。
按任意键关闭此窗口. . .

⭐️模拟实现qsort函数

经过对qsort函数的了解,我们发现该函数能够对多种数据类型进行排序取决于传入的函数指针参数。我们以冒泡排序为例,模拟实现qsort函数。

🍋冒泡排序改装思路

先来看看冒泡排序函数

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

//冒泡排序(int) 从小到大
void bubble_sort(int* arr, int size)
{
	int i = 0;
	for (i = 0; i < size - 1; i++)
	{
		int j = 0;
		int flag = 1;//判断数据是否发生交换,默认没有发生交换
		for (j = 0; j < size - 1 - i; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
				flag = 0;
			}
		}
		if (flag)
			break;
	}
}

如果需要对多种数据类型进行排序,对应上面的这个冒泡排序,它所接受的参数肯定是必须改的,因为要接受多种数据类型的指针,所以传入的指针必须是无具体类型void。函数内部中循环排序的次数是没有发生改变的,所以函数内部的两层循环是不用发生改变的,但是由于传进来的是void型指针,无法进行运算和解引用,所以判断语句是需要进行改动的。

🍋冒泡排序模拟实现qsort函数

对于if语句中所对应条件,我们可以调用compare函数,将数组的前一个元素与后一个元素比较,如果该函数返回值大于0,表示数组前面的元素比后面的元素大,如果进行升序排列,我们需要对这两个元素进行交换。这个交换我们不妨封装在一个swap函数里,由于不知道数据类型,所以swap函数的参数为两个元素的无类型地址,交换的时候我们不妨强制转换为char*类型,因为char类型大小为1字节,所有的数据类型的内存大小都是字符型内存大小是整数倍(只考虑内存大小,字符型可以理解为其他数据类型的最小单位)。根据需要交换元素的大小,我们一个一个地将每个单位字节的二进制序列交换,这样就完成了交换。对于如何得到相邻元素的地址,同理我们先可以将传入指针强制转换为char*类型,根据元素的内存大小,计算得出每个元素的地址。比如数组某个元素内存大小为width,则相邻两元素地址可以表示为(char*)val + j * width, (char*)val + (j + 1) * width
知道了冒泡排序哪个地方需要改装,我们来试着动手实践一下。

typedef unsigned int sz_t;
void bubble_qsort(void* val, sz_t size, sz_t width, int (*cmp)(const void* p1, const void* p2))
{
	sz_t i = 0;
	for (i = 0; i < size - 1; i++)
	{
		sz_t j = 0;
		sz_t flag = 1;
		for (j = 0; j < size - 1 - i; j++)
		{
			if (cmp((char*)val + j * width, (char*)val + (j + 1) * width) > 0)
			{
				swap((char*)val + j * width, (char*)val + (j + 1) * width, width);
				flag = 0;
			}
		}
		if (flag)
			break;
	}
}
void swap(char* buf1, char* buf2, sz_t width)
{
	sz_t i = 0;
	for (i = 0; i < width; i++)
	{
		char tmp = *(buf1 + i);
		*(buf1 + i) = *(buf2 + i);
		*(buf2 + i) = tmp;//交换每单位字节对于的二进制序列
	}
}

这样,冒泡排序就能排序多种数据类型,模拟实现了qsort函数,当然也可以使用其他的排序方法模拟实现qsort函数。

🍋测试

测试主函数

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main ()
{
//test;
}

测试1:整数排序

void test1()
{
	int arr[] = { 2,8,6,12,3,86,1,42,66,22,98,88 };
	sz_t number = sizeof(arr) / sizeof(arr[0]);
	sz_t size = sizeof(int);
	sz_t i = 0;
	printf("数组排序前:\n");
	for (i = 0; i < number; i++)
	{
		printf("%d ", arr[i]);
	}
	bubble_qsort(arr, number, size, compare);
	printf("\n数组排序后:\n");
	for (i = 0; i < number; i++)
	{
		printf("%d ", arr[i]);
	}
}
数组排序前:
2 8 6 12 3 86 1 42 66 22 98 88
数组排序后:
1 2 3 6 8 12 22 42 66 86 88 98
D:\gtee\C-learning-code-and-project\练习使用qsort\Debug\练习使用qsort.exe (进程 10620)已退出,代码为 0。
按任意键关闭此窗口. . .

测试2:双精度浮点数排序

void test2()
{
	double arr[5] = { 3.5,8.9,9.2,4.8,2.1 };
	sz_t number = sizeof(arr) / sizeof(arr[0]);
	sz_t size = sizeof(double);
	sz_t i = 0;
	printf("数组排序前:\n");
	for (i = 0; i < number; i++)
	{
		printf("%.2lf ", arr[i]);
	}
	bubble_qsort(arr, number, size, compare_dou);
	printf("\n数组排序后:\n");
	for (i = 0; i < number; i++)
	{
		printf("%.2lf ", arr[i]);
	}
}
数组排序前:
3.50 8.90 9.20 4.80 2.10
数组排序后:
2.10 3.50 4.80 8.90 9.20
D:\gtee\C-learning-code-and-project\练习使用qsort\Debug\练习使用qsort.exe (进程 34200)已退出,代码为 0。
按任意键关闭此窗口. . .

测试3:结构体中字符串排序测试

void test3()
{
	struct stu arr[3] = { {"zhangsan", 15}, {"lisi", 30},{"wangwu", 10} };
	sz_t number = sizeof(arr) / sizeof(arr[0]);
	sz_t size = sizeof(arr[0]);
	sz_t i = 0;
	printf("数组排序前:\n");
	for (i = 0; i < number; i++)
	{
		printf("%s ", arr[i].name);
	}
	bubble_qsort(arr, number, size, compare_stu);
	printf("\n数组排序后:\n");
	for (i = 0; i < number; i++)
	{
		printf("%s ", arr[i].name);
	}
}
数组排序前:
zhangsan lisi wangwu
数组排序后:
lisi wangwu zhangsan
D:\gtee\C-learning-code-and-project\练习使用qsort\Debug\练习使用qsort.exe (进程 13512)已退出,代码为 0。
按任意键关闭此窗口. .

关于qsort的内容就分享到这了!当然如果对快速排序感兴趣,可以自己使用其他的排序方法模拟实现qsort函数!还请大家多多支持!感谢感谢!

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。