qsort函数,学会就会排序

在c语言中我们经常会用到排序,排序的方法也有很多,对于一部分初学者来说最常用到的就是冒泡排序和选择排序,但是有一个函数可以帮助我们快速的排序

qsort函数,本文章适合学习过指针,或者对指针有了解的同学研讨

通过本文章的学习,可以自己编写自己的qsort函数

我们先来了解一下qsort函数

qsort函数是c语言编译器自带的排序函数,使用时对标准库函数stdlib.h进行声明

qsort函数的原型是

void qsort(void*base,size_t num,size_t width,int(*cmp)(const void*,const void*))

void qsort(void* base,表示需要排序内容的地址注意void*是没有办法直接解引用的 
size_t num,需要排序内容的个数 
size_t width,需要排序数组中的数据占据字节大小 
int(*cmp)(const void*,const void*)用来比较待排序数据中的2个元素 判断比较方式 比较方式需要使用者自己写一个函数来判断 
);

此处的int(*cmp)(const void*,const void*)表示一个函数指针

明白了qsort函数的组成之后我们来应用一下qsort函数

#include
#include
int cmp_int(const void*e1,const void *e2)
{
	return *(int*)e2-*(int*)e1;
 } 
int main()
{
	int arr[]={0,1,2,3,4,5,6,7,8,9};
	int i;
	qsort(arr,sizeof(arr)/sizeof(arr[0]),sizeof(arr[0]),cmp_int);
	for(i=0;i<10;i++)
	printf("%d",arr[i]);
	return 0; 
} 

在 cmp_int函数中返回类型要强制转化为(int*)型这是为什么呢?

我们在上文中提到过void*是没有办法直接解引用的,因为我们是对一个数组中的数字进行排序,所以我们直接转化为int*型

同样值得我们关注的点是return中返回值决定了我们是对一个数组中数据进行从大到小排序,还是从小到大排序。

如果返回值<0那么e1所指向的元素会被排在e2所指向的元素的左面

如果返回值=0那么e1,e2所指向的元素排序不确定

如果返回值>0,那么e1所指向的元素会被排在e2所指向的元素右面

随着我们日后的学习我们会发现qsort函数除了能对数字排序还可以对字符串,结构体关键字,结构体字符串等排序

那么了解了qsort函数的功能和用法之后你是否也想自己写一个自己的qsort函数呢?

接下来我们对自己的qsort函数进行编写

首先我们在此处应该明白最基本的排序就是冒泡排序,为了方便大家更好的理解,所以我们在自己编写时,对于排序的部分也使用冒泡排序

接下来我们对自己的qsort函数进行分析

qsort函数由三部分组成1.函数主体我们命名为bubble_sort2.交换函数swap3.比较函数cmp

1.bubble_sort(void*case,int sz,int width,int(*cmp)(const void*e1,const void*e2);

bubble_sort(void*case同qsort函数一样我们此处的void*case是一个无定型指针要求输入我们要排序内容的地址

int sz表示我们要排序内容的个数

int width表示我们要排序的内容占据的字节大小

int(*cmp)(const void*e1,const void*e2)此处我们插入一个函数指针指向我们定义的比较函数

特别注意的是因为我们此处只是进行计算,所以加const保证指针指向的值不会改变

);

void bubble_sort(void* base, int sz, int width, int(*cmp)(const void* e1, const void* e2))
{
	int i, j;
    //因为我们排序的方式仍然为冒泡排序所以我们采用冒泡排序的方式
	for (i = 0;i < sz - 1;i++)
	{
		for (j = 0;j < sz - 1 - i;j++)
		{
			if (cmp((char*)base+j*width,(char*)base+(j+1)*width)>0)
			{
				swap((char*)base + j * width, (char*)base + (j + 1) * width,width);
			}
		}
	}
}

我们发现if中的内容比较奇怪

if (cmp((char*)base+j*width,(char*)base+(j+1)*width)>0)

接下来我们分析这个if:因为我们不知道用户使用我们的这个函数对何种的数据进行排序,所以我们直接把需要比较的内容强制类型转化为(char*)变成单个的字节,再通过+j*width来确定我们每个元素的地址,同qsort函数一个原理如果我们比较函数的返回值时正数,那么我们对量数据进行交换。

比如:我们要排序int型的数据,我们知道int型数据占据4个字节,所以此时的width为4,当我们把整个数据都转化为(char*)的单个字节时我们+或者- 4或者4的倍数就表示+或者-一个整型数据的地址。

因此我们就通过这样的方式来进行两个元素之间地址的输入

2.swap((char*)base + j * width, (char*)base + (j + 1) * width,width);

根据上面的分析我们可以理解此处swap函数也通过同样的手法来进行地址的输入

在交换时因为我们无法判断到底用户使用什么样的数据进行交换,所以我们干脆不进行判断,而是选择更为简单粗暴的把字节交换,所以我们的swap函数就需要知道我们每次交换的次数即一个数据所占据的字节大小

void swap(char* buf1, char* buf2, int width)
{
	int i;
	for (i = 0;i < width;i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}

当我们对两个数据进行交换之后buf1和buf2分别++指向下面两个需要交换的数据

3. cmp((char*)base+j*width,(char*)base+(j+1)*width)

接下来我们对比较函数进行分析,通过上面的讲解我们已经明白(char*)的强制类型转换可以帮助我们输入需要比较两个元素的地址

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

 此处我们的cmp函数和qsort函数中的cmp函数作用相同,此处不再过多赘述。

通过上面对三个主要部分的分析我们已经明白了我们自己的qsort函数的组成和每部分的内容

接下来我们对自己的qsort函数进行应用

#include
int cmp(const void* e1, const void* e2)
{
	return *(int*)e1 - *(int*)e2;
}
void swap(char* buf1, char* buf2, int width)
{
	int i;
	for (i = 0;i < width;i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}
void bubble_sort(void* base, int sz, int width, int(*cmp)(const void* e1, const void* e2))
{
	int i, j;
	for (i = 0;i < sz - 1;i++)
	{
		for (j = 0;j < sz - 1 - i;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 main()
{
	int arr[] = {10,5,6,9,2,3,1,4};
	bubble_sort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(arr[0]), cmp);
	int i;
	for (i = 0;i < sizeof(arr) / sizeof(arr[0]);i++)
		printf("%d", arr[i]);
	return 0;
}

如果自己能写一个自己的qsort函数是不是很有成就感呢?好了,本篇文章到此结束

谢谢大家,如果大家有更好的想法欢迎大家留言,或者在评论区相互探讨!

你可能感兴趣的:(c语言,排序算法,开发语言,算法)