c语言调整数组数的顺序_剑指offer:调整数组顺序使奇数位于偶数前面

目录

  • 题目

  • 分析

  • 解法一

  • 解法二

  • 解法三

  • 运行效率比较

  • 扩展

  • 总结

题目

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。

分析

事实上,这个题比较简单,很多种方式都可以实现,但是其时间复杂度或空间复杂度不尽相同。

解法一

书中作者提到一种初始的做法是,从头扫描整个数组,如果遇到偶数,则拿出这个数,并且把整个数组的数据都向前挪动一位,再把拿出的数放到末尾。每碰到一个偶数就需要移动O(N)次,这样总的时间复杂度为O(n^2),空间复杂度为O(1)

这种方式很简单,如果已经很清楚是怎么回事,可以跳过例子说明,继续阅读下一个解法。但是可以尝试自己写一下代码,发现有些细节部分并不是那么容易写出来

举个例子,假设有数据1,2,3,4,5,6:

从左往右扫描,找到第一个偶数2,并临时保存:

123456
取出

将2后面的所有数往前移动一个位置,并将2放到最后一个位置:

134562
移动后

继续扫描当前位置,发现3为奇数,继续,发现4为偶数,将从3之后位置的数开始,到倒数第二个位置,所有数往前移动一个位置,并将4放到最后:

135642
移动后

继续扫描当前位置数5,6,至此,偶数有2两个,当前指向位置为,所在下标为4,总数 - 位置  <= 偶数 ,结束。

135642

根据该思路,C语言代码实现如下:

//reorder.c

解法二

很多人其实最先想到的解法可能是,创建一个新的数组,从头扫描,遇到偶数放后边,遇到奇数放前边。扫描结束后,再将数组内容拷贝到原数组,这样整个时间复杂度为(n),而空间复杂度也为O(n),这样的方法实现简单,也不容易出错。C语言代码实现如下:

//reorder1.c

解法三

还记得我们之前介绍过的《快速排序优化详解》吗?快速排序中,有一个分区操作,是将整个数组大于基准的部分,放右边,而小于基准的部分放右边,即根据基准,将数组一分为二。其实在这里,同样可以参考这个思路,只不过跟基准比大小,变成了判断是奇还是偶。
这里简单描述一下该思路,更多细节可以参考《快速排序优化详解》中如何将元素移动到基准两侧一节:

  • 定义下标i和j,分别从开头和结尾开始扫描

  • 当i遇到偶数时,停止扫描

  • 当j遇到奇数时,停止扫描

  • 此时交换i和j位置的值

  • 继续前面的操作,直到i和j交错或相等

举个例子,假设有数据1,2,3,4,5,6,7,8:

f397a6ce189836b0fd2af60788944885.png

i和j继续扫描,i遇到2停止,j遇到5停止,交换两处的值:

bd87e5b5cd0ae80ea1036ab2aad89969.png

i和j继续扫描,i遇到4停止,j遇到5停止,交换两处的值:

8511e515da9f1f8f059bdc071a09b60b.png

继续扫描,此时,i和j交错,扫描结束:

442c0383b43385ec8aa60b18ff44be1b.png

基于该思路的算法时间复杂度为O(n),空间复杂度为O(1),C语言代码实现如下:

//reorder2.c

运行效率比较

编译后,对一百万数据进行操作,运行时间结果如下。
解法一:

time ./reorder 

并没有耐心等到结果出来。
解法二:

100000000

对1亿数据进行操作,耗时很短,只是内存占用较多。
解法三:

100000000

耗时很短,内存占用少。

扩展

在本题中,只是对整数是奇还是偶进行分开,那么如果是别的条件呢?例如是否为素数,是否为正数等等。我们可以让调用者传入一个条件函数,让它决定到底是放在后半部分,还是前半部分。这是不是很向库函数qsort需要传入一个比较函数的做法?这部分内容可以参考《函数指针》,根据这个思路,我们修改解法三的代码:

左下角阅读原文获取完整代码

这个时候通过传入函数指针,可以对任意条件进行处理了。

总结

我们发现,一些基本算法的思想,通常可以用到其他问题上,而不同的思路,带来的效率可能天差地别。

练习

尝试自己实现上面的算法并扩展实现。

备注

完整代码以及模拟一亿数据处理请点击左下角【阅读原文】访问(电脑访问更佳)。

地址:

https://www.yanbinghu.com/2019/02/28/31098.html

关注公众号【编程珠玑】,获取更多Linux/C/C++/Python/Go/算法/工具等原创技术文章。

a6aca2f9d8e354f6c938cafd337a8e70.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值