IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 怎么样才能做到对多种数据类型排序?C语言快速排序——qsort函数及其模拟实现 -> 正文阅读

[Java知识库]怎么样才能做到对多种数据类型排序?C语言快速排序——qsort函数及其模拟实现

??前面的话??

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

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


??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函数!还请大家多多支持!感谢感谢!

觉得文章写得不错的老铁们,点赞评论关注走一波!谢谢啦!
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-09-10 10:43:56  更:2021-09-10 10:44:25 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 16:49:57-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码