万能的排序“qsort”

时间:2023-01-28 01:05:04

万能的排序“qsort”

今日份学习“qsort”函数

前言:

针对一个数组的元素进行排序,我们可以想到冒泡排序法,且如果将该排序法专门写入一个函数,需排序的数组调入其中是不是方便很多呢?且如果要实现这样的函数,我们需考虑哪些问题呢?(可以自己先想一想再往下看哦)

一,实现条件

C语言中“qsort”函数便是能实现各种数组排序的函数,来看他的实现条件

万能的排序“qsort”

1,返回值:qsort函数首先是无返回值的

2,void* base:接收一个数组地址,为什么是空类型呢?因为该函数的创建者他并不知道使用者将会使用什么类型的数组且设置为空类型指针以便于接收任意类型的地址。

3,size_t num:数组的元素个数,以无符号整型的方式来接收使用者传递过来的数组个数,首先确保数组个数不能是负数

4,size_t size:数组元素的大小,同样以无符号整型的方式接收使用者传递过来的数组元素的大小,且单位是字节

5,int (*compar)(const void*,const void*):数组元素的比较方式,这是整函数中最核心的部分,我们要比较两元素的大小,首先确定其元素是什么类型,如果是整型可以用“><”的方式进行比较,如果是字符型则需要借用“strcmp”函数的返回值进行比较且以及其它各种类型都有其各种的比较方法。

所以qsort函数该参数部分就是接收使用者自己先创建其数组元素的比较方法函数,再将其函数指针作为参数进行传导。

特别声明一点:使用者创建的比较函数必须按照:

int (*)(const void*,const void*)的格式进行创建

以下便是官方的解释条件:

万能的排序“qsort”

二,使用方法

了解了qsort的使用格式,接下来我们试着去使用该函数。

1,创建一个数组:int arr[]={9,8,3,7,1,6,8,2,1,0};

2,计算数组长度:size_t num=sizeof(arr)/sizeof(arr[0]);

3,计算元素大小:size_t size=sizeof(arr[0]);

4,创建比较函数:

因为我们的数组是整型,则需创建整型大小的比较方式

且按照int (*)(const void*,const void*)的格式进行创建

若返回值大于0(e1>e2),返回值小于0(e1<e2),返回值为0(e1==e2)

//创建整型比较函数

int compar (const void*e1,const void*e2)
{
	//整型的比较方式
  return (*(int*)e1-*(int*)e2);
}


5,代入qsort函数:qsort(arr,num,size,compar);

6,实现结果:

使用qosrt函数引用头文件<stdlib.h>

#include<stdio.h>
#include<stdlib.h>
int compar(const void* e1, const void* e2)
{
	//整型的比较方式
	return (*(int*)e1 - *(int*)e2);
}
int main()
{
	int arr[] = { 9,8,3,7,1,6,8,2,1,0 };//创建数组
	size_t num = sizeof(arr) / sizeof(arr[0]);//数组长度
	size_t size = sizeof(arr[0]);//元素大小
	qsort(arr, num, size, compar);//代入qsort
	for (int i = 0; i<(int)num; i++)//打印
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

万能的排序“qsort”你学废了嘛????

三,自主实现

学会了怎么使用qsort函数,能否自己也实现一个qsort函数呢?

当然别人能做到,我们肯定也能做到!

实现一个数组的排序:

数组的地址,数组的长度,数组元素大小,元素的比较方式。

void my_qsort(void* arr,size_t num,size_t size,int (*tmp)(const void*,const void*)

排序方式:

这里我们在内部实现冒泡排序:

//常规的冒泡排序法
for(i=0;i<num-1;i++)
	for(j=0;j<num-i-1;j++)
  {
  	if(arr[j]>arr[j+1])
    {
    	arr[j]=arr[j+1];
    }
  }

当然常规的冒泡排序肯定不能应用在我们的”my_qsort“函数中

因为该冒泡只针对整型的排序,如何应用于各种类型的排序呢?

我们就得在该基础上去改变它,让其排序更全能性。

for(i=0;i<num-1;i++)
	for(j=0;j<num-i-1;j++)

这两循环控制数组的下标(所有的元素的下标都通用),这个可以不用去改变。

if(arr[j]>arr[j+1])

该比较方式肯定不通用于所有元素,则将该代码改成更通用。

这里作为创建者的视角肯定是不知道使用者是比较什么类型的元素。

这里就需要使用者自己创建针对自己数组的比较函数,将该函数指针传递给我们。

则改为:

if(tmp( ((char*)arr+j*size),( (char*)arr+(j+1)*size)>0 )

tmp:是使用者传递比较函数的函数指针

我们利用该函数指针将arr数组的各个元素作为它的参数进行传址

作为创建者且不知道使用者是什么类型的元素,但可以根据使用者传递的元素大小来决定该元素的字节。

((char*)arr+j*size):将首元素地址强制转换为字符指针类型,再加上元素大小的字节个数相当于原本的元素类型走一步的字节数。j代表该数组的第j个元素

( (char*)arr+(j+1)*size) ):同上,j+1代表该数组第j后一个元素

将两个元素的指针代入tmp函数进行比较。

再根据tmp的返回值来决定两个数谁大谁小,根据升序或者降序对该元素进行互换。

那如何实现交换呢?

因为目前要对两个元素进行调换,则根据元素的大小进行多少字节的调换

则可以利用循环来进行对两数进行调换

//创建一个交换函数

void swap(void* e1,void* e2, size_t size)//交换函数
{
	for (int x = 0; x < (int)size; x++)
	{
		char y = 0;
		y = *(char*)e1;
		*(char*)e1 = *(char*)e2;
		*(char*)e2 = y;
		((char*)e1)++;
		((char*)e2)++;
	}
}

来看看总体效果:

#include<stdio.h>
#include<string.h>
int tmp(const void* e1, const void* e2)//使用者的比较函数
{
	return strcmp((char*)e1, (char*)e2);
}
void swap(void* e1,void* e2, size_t size)//交换函数
{
	for (int x = 0; x < (int)size; x++)
	{
		char y = 0;
		y = *(char*)e1;
		*(char*)e1 = *(char*)e2;
		*(char*)e2 = y;
		((char*)e1)++;
		((char*)e2)++;
	}
}
void my_qsort(void* arr, size_t num, size_t size,int(*camp)(const void*,const void*))
{
	int i = 0, j = 0;
	for (i = 0; i <(int) num - 1; i++)
		for (j = 0; j < (int)num-1 - i; j++)
		{
			if (tmp(((char*)arr + j * size), ((char*)arr + (j + 1) * size))>0)
			{
				swap(((char*)arr + j * size), ((char*)arr + (j + 1) * size), size);
			}
		}
}
int main()
{
	char arr[] = { 'a','c','f','d','e','b','g','i','h','k','g' };//创字符数组
		size_t num = sizeof(arr) / sizeof(arr[0]);//数组长度
		size_t size = sizeof(arr[0]);//数组元素大小
		my_qsort(arr, num, size, tmp);//代入my_qsort
		for (int i = 0; i < (int)num; i++)//打印
	{
		printf("%c ", arr[i]);
	}
}

万能的排序“qsort”还是难不倒我们的????

✨撒花❀✨

结束语:

经验环境和遗传造就了你的面目,无论是好是坏,你都得耕耘自己的园地;无论是好是坏,你都得弹起生命中的琴弦。

一分耕耘一份收获加油~

万能的排序“qsort”