[C语言]使用 qsort快速排序函数,和复现qsort(冒泡方法)

在对数组进行排序的时候,很多的初学者都是使用冒泡排序,但是冒泡排序的效率却不高,而且还需要自己动手,没错我就是懒,并只能排序数字类型的数据

而qsort基本上所有的数组都能够排序,可见其强大;

冒泡排序如下:

#include <stdio.h>
//冒泡排序函数
int* bubble_sort(int* arr, int sz)
{
	for (int i = 0; i < sz - 1; i++)
	{
		for (int 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;
			}
		}
	}
}

//打印数组函数
void print_arr(int arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}
int main()
{
	int arr[5] = { 1,45,2,62,3 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr, sz);
	print_arr(arr, sz);
	return 0;
}

在这里插入图片描述

如果我们使用的是库函数qsort呢。

qsort函数(快速排序函数)

使用qsort函数需要导入 stdlib.h 这个头文件

这是qsort函数的组成

void qsort( void * base ,

size_t num ,

size_t width ,

int(__cdecl * compare )(const void* elem1 , const void* elem2));

从以上可以看到,如果我们想要调用这个函数,需要有四个参数:

void* base 是我们要排序数组的指针。

size_t num 是我们要导入数组的数组长度,单位是byte。

size_t width 是数组单个字节的宽度;比如:char类型的数据宽度是1,int类型的宽度是4。

int(__cdecl * compare )(const void* elem1 , const void* elem2) 是一个函数指针,注意函数实参都必须是void* 类型的。在这个函数指针中会进行比较:看下图

可能大家看上面的解释觉得很苦涩,我们直接在代码中学习吧

#include <stdio.h>
#include <stdlib.h>
int cmp_int(const void* e1, const void* e2)
{
	return *(int*)e1 - *(int*)e2; //我们比较的是int类型的数组,先强制类型转换为(int*)类型
			//如果是整型类的数据,我们可以直接用相减来判断我们返回的值;
			//如果e1比e2大,则返回大于零,如果小,就是小于0,相等的话就是0; 
}
int main()
{
	int arr[10] = { 1,3,5,7,9,0,2,4,6,8 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cmp_int);
	return 0;
}

在第七行我们定义了一个数组,然后第八行求出了整个数组的长度,

第九行使用qsort函数 , 在函数中(函数首元素地址,数组长度,数组宽度,函数指指针)

注意函数指针cmp_int 是我们由创建的,并且其中的参数是固定的,返回类型必须是int,我们要排序什么类型的数组,就要在 这个函数中强制转换

现在我们来一些深入的内容

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

//我们定义的结构体,代表考试的成绩
struct Exam {
	char name[20];
	float math;
	int  sum;
};

//比较 数学 成绩的函数
int cmp_math(const void* e1, const void* e2) //float类型的数值是不能相减的,会照成精度丢失
{					//所以采用原始一些的方法:比较
	if (((struct Exam*)e1)->math > ((struct Exam*)e2)->math) //注意,因为是结构体类型,所以要转化为结构体指针
		return 1;
	else if (((struct Exam*)e1)->math < ((struct Exam*)e2)->math)
		return -1;
	else
		return 0;
}

//比较 总成绩 的函数
int cmp_sum(const void* e1, const void* e2)
{
	return ((struct Exam*)e1)->sum - ((struct Exam*)e2)->sum;//整数类型采用相减
}

//比较 名字 的函数
int cmp_name(const void* e1, const void* e2)
{
	return strcmp(((struct Exam*)e1)->name, ((struct Exam*)e2)->name);//字符串的话我们使用了一个strcmp的库函数
}									//需要引入头文件 string.h

//打印数组
void print_arr(struct Exam arr[], int sz)
{
	for (int i = 0; i < sz; i++)
	{
		printf("%s   %f   %d\n", arr[i].name, arr[i].math, arr[i].sum);
	}
}

//函数主体
int main()
{
	struct Exam arr[] = { {"lihua",85.6f,253} ,{"huiyun",99.9f,299},{"zhangli",86.6f,266} };
	int sz = sizeof(arr) / sizeof(arr[0]);
	//my_qsort(arr, sz, sizeof(arr[0]), cmp_name);  //排序名字
	qsort(arr, sz, sizeof(arr[0]), cmp_math);	//排序数学成绩
	//qsort(arr, sz, sizeof(arr[0]), cmp_sum);	//排序总成绩

	print_arr(arr, sz);
	return 0;
}

我们先打印 math 的排序,其他类型也一样,

排序数学成绩

当能看懂这个函数时,我们就可以再次深入一些,复现qsort函数

复现qsort 函数

我所复现的qsort 函数和原函数的排序方法是不同的, 原函数使用的是快速排序,我使用的是冒泡方法来构造这个函数。

因为我们的qsort 函数时所有类型都能排序的,那它接收的类型是什么呢?没错就是 void* ,我们看下void*的定义

void* 是一种无类型的指针,无具体类型的指针
void* 的指针变量可以存放任意类型的地址
void* 的指针不能直接进行解引用操作
void* 的指针不能直接进行+—整数

总而言之,void* 就像一个垃圾桶,什么类型的指针都可以往里扔。

直接看代码,还是沿用了之前的代码,我就是懒,但并不是所有代码,只是核心代码,我们分析这里即可。

#include <stdio.h>
#include <string.h>
struct Exam {
	char name[20];
	float math;
	int  sum;
};

//我们传入的类型现在已经是固定的 char* 了,所以指针类型在这只能用 char* 接收
//我们在这个函数中是一个字节一个字节的交换,并不是整个元素的交换,因为只有这样,才能适用所有类型
void Swap(char* p1, char* p2,int width)
{
	for (int i = 0; i < width; i++)
	{
		char tmp = *p1; 
		*p1 = *p2;
		*p2 = tmp;
		p1++;
		p2++;
	}
}


//这就是我们自己构造的 my_qsort 函数
//1.得到数组长度 sz,因为我们需要得到的边间
//2.得到元素宽度 width,因为只有得到了元素的宽度才能在数组内进行跳转。
//3.得到数组的首元素地址void * base,注意是void* 因为只有得到了首元素地址,我们才能结合元素宽度,得到数组中每个元素的相隔的距离,
//4.的到函数指针 int (*cmp)(const void* e1, const void* e2) ,只有得到函数指针,才能将我们的数组中的每个元素进行比较
void my_qsort(void* base, int sz, int width, int (*cmp)(const void* e1, const void* e2))
{
	for (int i = 0; i < sz - 1; i++)
	{
		for (int j = 0; j < sz - i - 1; j++)
		{   
			if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)  //将它们都强制换化为 char* 类型。 是因为 char* 是最小的字节,而且每次加1的话,只会移动一个字节 
			{                                                               //而将 j *width 是因为width的宽度是随着我们导入的类型决定的,这里比较的像是 a[j]>a[j+1]

  
				Swap((char*)base + j * width, (char*)base + (j + 1) * width,width); //这个就是交换函数了,同时也限定了交换的范围
			}
		}
	}
}

以上就是我们核心代码的分析,以是我尽力而为,若有不懂,可以私信我,免费解答

接下来我们运行下所有代码,也就只是加上了,那些比较函数

#include <stdio.h>
#include <string.h>
struct Exam {
	char name[20];
	float math;
	int  sum;
};
void Swap(char* p1, char* p2,int width)
{
	for (int i = 0; i < width; i++)
	{
		char tmp = *p1; 
		*p1 = *p2;
		*p2 = tmp;
		p1++;
		p2++;
	}
}
void my_qsort(void* base, int sz, int width, int (*cmp)(const void* e1, const void* e2))
{
	for (int i = 0; i < sz - 1; i++)
	{
		for (int j = 0; j < sz - i - 1; j++)
		{
			if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0) 
			{
				Swap((char*)base + j * width, (char*)base + (j + 1) * width,width);
			}
		}
	}
}
int cmp_math(const void* e1, const void* e2)
{
	if ( ((struct Exam*)e1)->math > ((struct Exam*)e2)->math) //因为score里的数据是float类型的,所以不能相减
		return 1;											// 若 0.9- 0.8 = 0.1,而我们的返回类型是int型,返回的值就会变成int
	else if (((struct Exam*)e1)->math < ((struct Exam*)e2)->math)//使用这里不能使用减号,要使用关系运算符(也就是 比较 )
		return -1;
	else
		return 0;
}
int cmp_sum(const void* e1, const void* e2)
{
	return ((struct Exam*)e1)->sum - ((struct Exam*)e2)->sum;
}
int cmp_name(const void* e1, const void* e2)
{
	return strcmp(((struct Exam*)e1)->name, ((struct Exam*)e2)->name);
}
void print_arr(struct Exam arr[], int sz)
{
	for (int i = 0; i < sz; i++)
	{
		printf("%s   %f   %d\n", arr[i].name,arr[i].math,arr[i].sum);
	}
}
int main()
{
	struct Exam arr[] = { {"lihua",85.6f,253} ,{"zhangli",86.6f,266},{"huiyun",99.9f,299} };
	int sz = sizeof(arr) / sizeof(arr[0]);
	my_qsort(arr, sz, sizeof(arr[0]), cmp_name);
	//my_qsort(arr, sz, sizeof(arr[0]), cmp_math);
	//my_qsort(arr, sz, sizeof(arr[0]), cmp_sum);

	print_arr(arr, sz);
	return 0;
}

这次我们运行的是 name 的排序:

name排序

字符串排序是根据ascii码表来排序的。

本次的分享就到这里,若有错误,欢迎大家指正,若此问题不懂,可以私信我,无偿解答。

愿你我都能在编程的道路上越走越远!!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值