冒泡排序与qsort函数

一、冒泡排序

1、常规:

冒泡排序是排序问题中一种比较常用的方法。

来看一个例子:

我们给一个乱序的整型数组:int arr[10] = {5,6,7,4,9,1,8,10,2,3}

将其升序排列

这时候我们就可以用到冒泡排序,其核心思想为两两相邻元素进行比较。

遵循这个思想,我们能想到用遍历数组的方式来一趟一趟比较,那么在一趟完整的比较后假设我们确定好最右边数字即最大数字的位置,那么我们再来一趟就排好第二大的数字,以此类推我们只需要排序数组长度-1次,剩下的最后一个自动归位。

而对于我们的每一趟比较,我们要从左到右遍历找到那个大数字,那么每一趟需要比较几次呢,对于第一趟,我们要将10个数全部比较,两两相邻进行比较,如果左边大那么就交换位置继续比较,因此我们确定第一个最大数需要比较9对 第二次时需要比较8对 以此类推

我们在代码上来进行实现:

经过我们的分析和代码实现成功得到了正确的结果,我们上面排的是升序,那么可不可以排成降序呢 当然可以 在升序交换的过程中我们只要将>改成<即可  来看效果

于是我们可以很轻松地实现数组的排序 

2、改进:

但是,显然函数还没有达到最优状态,因为我们发现 如果在中间的某一次排序后就已经变成正确的顺序,那么后面的几次排序就纯粹浪费时间了 因此我们可以考虑在排序中加个判断 即如果在某一次排序后顺序没有发生变化,说明排序已经完成,此时终止排序

如此改进后,可以节约程序运行时间

二、qsort函数

上面的排序方法能够帮我们解决一些问题,但我们发现在实际过程中给出的数据可能多种多样,有整型,字符型,结构体型等等各种类型的数据,那么我们如果需要排序各种各样的数据,怎么办呢,这里就需要用到qsort函数

1、介绍

qsort函数是库函数,可以直接使用。qsotr函数是一种快速排序的方法并且qsort函数可以排序任意类型的数据。

上面介绍的排序方法存在一定问题,因为并不是所有的数据类型都能直接使用> <等比较符号来直接比较,而同时交换变量时到底创建什么类型的变量也是一个未知的问题,因此我们用到qsort函数。   这是cplusplus.com官网给出的qsort函数的介绍

我们来分析一下其参数以及返回类型

首先void* base base中存放的是待排序数组的第一个元素的地址,因为我们不知道其类型,用void*来代替 后面可以强制类型转换

size_t num num表示的是base指向的数组中的元素个数

size_t size size表示base指向的数组中的一个元素的大小,单位是字节

int(*compar)(const void*,const void*) 这是一个函数指针,其指向一个比较函数,这个比较函数可以用来比较数组中两个元素

        我们设函数指针指向参数第一个为e1,第二个为e2

        如果e1指向元素大于e2指向的元素,返回一个大于0的数字

        如果e1指向元素等于e2指向元素,返回0

        如果e1指向元素小于e2指向的元素,返回一个小于0的数字

2、使用

有了如上对qsort函数的理解,我们来尝试使用qsort函数比较一下

先来看最简单的比较整型数组

在这个例子中,我们向qsort函数中传递了一个比较整型的函数compare_int 

那么,我们来看一下其他类型数据的比较

结构体类型比较:我们首先创建一个结构体变量包含名字和年龄

我们先来按名字比较:

如图所示,我们创建好结构体变量后引入了比较字符串的函数compare_struct_name  

在比较字符串的过程中我们需要用到strcmp函数 参数1大于参数2返回大于0的数字 以此类推

字符串的比较从第一个字符开始,比较他们的ASCII码

而对于参数由于qsort参数为void*类型 我们需要先转换成struct stu*类型后再解引用访问成员变量

我们来调试观察下排序是否发生变化:

当我们走完qsort函数后发现,确实成功排序了,以上是按结构体中的名字来排序

那么我们想要以结构体中的年龄来排序呢,当然可以

调试后发现排序成功

如果你看到这,恭喜你,我们初步掌握了qsort函数的排序方法 那么这么神奇的函数你想不想自己实现一下呢,跟着我一起来看。

三、qsort函数的模拟实现

我们知道qsort函数是用来排序任意类型的数组的

那么在交换顺序的时候有一个问题:我们并不知道传递过来的数据是什么数据类型,因此很难实现交换代码的过程。那么这个时候我们就要从更底层来思考了,我们知道整型占4个字节,char类型占1个字节,浮点数float占4个字节等等,既然我们不能直接交换数据,因为不明确数据所占的字节大小,那么我们能不能直接交换字节呢,不管数据是什么类型,只要我们将数据所占的全部字节交换,那么是不是就实现了这个功能呢,跟着我来一起实践!

在交换过程中,我们需要上文介绍的冒泡排序思想,即两两相邻之间快速排序

我们先来写一个交换函数 要想一个字节一个字节交换,需要使用char类型

此处交换buf1与buf2,width是一个数据所占的字节大小 将<width作为循环结束条件,确保每一个字节都能被交换

接下来我们实现主体框架,判断交换条件

首先我们搭建好排序的框架,就是比较趟数与每趟比较次数,思想与冒泡排序相同

那么满足什么条件我们就要交换呢,如果要排成升序的话,我们原来在冒泡排序中写的是

if arr[j]>arr[j+1]  来判断整型数据 这里也要判断该数据,虽然我们不知道类型,但传参的时候传过来了每个数据的字节大小size

因为要交换字节,我们首先将base转成char*类型,那么其就变成了所有数据的第一个字节的地址

那么我们给(char*)base加上j乘以数据大小size是不是就得到了是不是就得到了第j个数据的起始地址,同理(char*)base加上j+1乘以数据大小size得到了第j+1个数据的起始地址

于是我们得出如下判断条件:

compar是我们函数指针所指向的排序函数 >0表面排成升序

满足这个条件,我们就进行交换,于是我们得到了完整的如下代码:

那么我们来测试一下模拟实现的my_qsort是否正确

如图,经过检验,我们的qsort函数模拟成功。

当然我们的代码可以完善一些,比如加入断言函数中传入的指针为非空指针等等,这里不再过多赘述,感谢你观看到这里!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值