冒泡排序和qsort函数详解以及如何模拟实现qsort函数

一.冒泡排序

冒泡排序是一种常见的排序方式,它可以把数组元素有序或无序的数组进行重新排序,并使得数组中的元素从大到小或从小到大进行排序(就像泡泡一样)。

冒泡排序原理:

每次比较数组中的相邻的两个元素的值,将较小的元素排在较大的元素的前面,就可实现数组元素从小到大排序;每次将较大的元素排在较小的元素的前面,就可实现数组元素从大到小排序。

以数字1,2,7,3,9,6为例,使其从大到小排序

对上表进行分析:

   第一次排序将最大的数字移动到第一第一元素[0]的位置,并将其它数字以次向后移动;第二次排序中,从第二个数字开始从剩余数字中挑选最大的数字,并将其移动到第二个位置,剩余数字依次向后移动;以此类推,可将数字1,2,7,3,9和6从大到小排列。

代码实现:

#include<stdio.h>

int main()
{
	int arr[10] = { 0 };
	int i = 0, j = 0, tmp = 0;
	for (i = 0; i < 10; i++)
	{
		scanf("%d", &arr[i]);//依次输入你想排列的数字
	}
	for (i = 9; i >= 1; i--)
	{
		for (j = i; j >= 1; j--)
		{
			if (arr[j] > arr[j - 1])
			{
				tmp = arr[j];
				arr[j] = arr[j - 1];
				arr[j - 1] = tmp;
			}
		}
	}
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
	
	return 0;
}

对上述代码进行分析:

我们可以看到总共有6个数字元素,单却只排序5次。在最后依次排序过程2与1交换了位置刚好满足从大到小排列,也就是说该数组你设置了n个元素那就要排序n -1次。

那依次排序中数组中的元素交换了多少次呢?

由图表可知,在第一次排序中,我们可以观察到【6,9】,【9,3】,【9,7】,【9,2】,【9,1】格一次总共是五次;在第二次排序中,因为我们已经知道9为最大值并且排在了[0]位置,所以9可以不用交换了,只需交换9后面的数字元素即可,这时总共交换了4次。以此类推可知第五次只需交换一次。由此可知,每次排序之后,数组元素需交换的次数都会减一。所以在代码实现中我套用了两层循环一层for(i=9;i>=1;i--)就是要排序的次数,另一层for(j=i;j>=1;j--)就是每次排序中需交换的次数。而j>=1这个判断条件是为了防止数组越界的。(因为如果j=0的话arr[j-1]就越界了)

二.qsort函数详解

    qsort函数是C语言标准库中的一种库函数,它的作用就是快速排序,而要使用它的话就需要引用头文件#include<stdlib.h>。

    qsort的函数原型:

    void qsort (void* base,size_t num,size_t size,int (* compar) (const void*e1,const void*e2)

    现在对qsort函数原型进行分析:

    1.void qsort:

    这个void代表qsort函数是一个无返回类型的函数。

    2.void* base:

    这个void*代表着是base的类型。base被官方解释为指向要排序的数组的第一个对象的指针,转换为空*。也就是说base,其实就是数组首元素的地址。

    具体实现:int values[6]={40,10,100,90,20,25};

                      qosrt(values,6,sizeof(int),compare);

    3.size_t num:

    这个size_t代表着是num的类型(size_t其实就是unsigned int 无符号整型),在官方的解释中num是按基数指向数组中的元素数,也就是说num就是你传给qsort这个函数的数组中的元素个数

    具体实现: qosrt(values,6,sizeof(int),compare);这个6就是num。

    4.size_t size

    size_t同上,size在官方的解释中就是数组中每个元素的大小(以字节为单位)。

    具体实现: qosrt(values,6,sizeof(int),compare);这个sizeof(int)就是int类型的值在内存中占多少个字节,因为数组values就是int类型的数组。

    5.int (*compare)(const void*e1,const void*e2)

    现在对上式进行分析。这是一个返回值类型为整型的函数指针,其中compare函数的两个参数的类型为const void*。而在官方的解释中这是一个指向比较两个元素大小的函数指针。

    在qsort函数中的compare函数的使用方式类似于strcmp函数,qsort函数主要是通过compare函数的返回值来判断传入compare函数的两个元素的值的大小关系。如果返回值小于零的话那么元素e1就小于e2;如果返回值等于零的话e1==e2;如果返回值大于零的话e1就大于e2。

    也就是说compare函数就是一种比较方法,用来比较传入compare函数的两个值的大小的。

 这个图片就是官方给出的解释。

综上,qosrt的使用就是这样的:

qsort(数组名,数组元素个数,数组每个元素的大小(以字节为单位),比较函数)

代码实现:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>

int compare(void* e1, void* e2)
{
	return (*(int*)e1 - *(int*)e2);//把e1,e2强制转换为(int*)类型的因为传到数组的元素就是int类型的。
}

int main()
{
	int arr[10] = { 0 };
	for (int i = 0; i < 10; i++)
	{
		scanf("%d", &arr[i]);
	}
	qsort(arr, 10, sizeof(arr[0]), compare);//总结就是 qsort(数组名,数组元素个数,数组每个元素的大小(以字节为单位),比较函数)
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

 

运行结果:

 

三.qosrt函数的模拟实现

通过观察qsort函数的代码实现以及函数原型,我们可以仿照出一个类似的函数,这个函数必须要具备qsort函数的所有功能。

我先设这个函数名称为bubble_sort,很多人可能会问,为什么我要把冒泡函数与qsort函数一起讲?等会你们就知道了。

我们既然要让bubble_sort实现qsort的功能那么在bubble_sort函数内部必然有一个排序语句来帮助bubble_sort这个函数正真实现快速排序,而冒泡排序法就是这个函数内部的的排序语句。但是qsort函数的模拟实现最主要也是最难的就是数组元素的交换,因为它涉及到了函数指针和强制类型转换,不多bb直接上代码然后解释一下具体怎么交换数组元素。

代码实现:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>

int compare(void* e1, void* e2)
{
	return (*(int*)e1 - *(int*)e2);
}
void bubble_sort(int* p, size_t num, size_t size, int(*compare)(void* e1, void* e2))
{
	int i = 0, j = 0;
	for (i = num - 1; i >= 1; i--)
	{
		for (j = 0; j < i; j++)
		{
			if (compare((char*)p+j*size,(char*)p+(j+1)*size) > 0)
			{
				int x = 0;
				char tmp;
				for (x = 0; x < size; x++)
				{
					tmp = *((char*)p + j * size+x);
					*((char*)p + j * size+x) = *((char*)p + (j + 1) * size+x);
					*((char*)p + (j + 1) * size+x) = tmp;
				}
			}
		}
	}
}
int main()
{
	int arr[10] = { 0 };
	for (int i = 0; i < 10; i++)
	{
		scanf("%d", &arr[i]);
	}
	bubble_sort(arr, 10, sizeof(arr[0]), compare);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

为了使我们模拟实现的bubble_sort函数能够排序多种类型包括整型(int,short,long,long),字符型(char)以及实型也叫浮点型(float,double),所以我们需要把数组元素强制转换为char*类型。

在上述代码中我们可以看到compare((char*)p+j*size,(char*)p+(j+1)*size),这里传过去的的是(char*)p+j*size和(char*)p+(j+1)*size,在这里如果我们只传(char*)p是显然行不通的,因为p这个地址就是数组arr的首元素地址,因为我们需要交换数组里面的所有元素所以我们就要传两个相邻的数组元素,而这两个元素会随着循环次数增加而改变。如j=0是(char*)p+j*size实际上就是arr[0],(char*)p+(j+1)*size显然就是arr[1]了,而随着j增加传过去的元素也会改变。(其中size就是数组元素的大小,而(char*)p加了j*size,地址p就会向后移动j*size个字节,也就是arr[]的下标会移动加j)。

而*((char*)p+j*size+x)和*((char*)p+(j+1)*size+x)其实就是对(char*)p+j*size+x以及后者的解引用而言,这里值得注意的是x,为什么要加x?是因为p已经被强制类型转换成了char*型的所以我们为了交换数组元素就要一个字节一个字节的交换(char类型占内存是1个字节,int是4个字节)。而且这个加x就是使p向后位移x位。

ps:compar函数传过去的参数应该是两个指针。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

东辰良月2

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值