剑指offer:调整数组顺序使奇数位于偶数前面
题目
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。
分析
事实上,这个题比较简单,很多种方式都可以实现,但是其时间复杂度或空间复杂度不尽相同。
解法一
书中作者提到一种初始的做法是,从头扫描整个数组,如果遇到偶数,则拿出这个数,并且把整个数组的数据都向前挪动一位,再把拿出的数放到末尾。每碰到一个偶数就需要移动O(N)次,这样总的时间复杂度为O(n^2),空间复杂度为O(1)。
这种方式很简单,如果已经很清楚是怎么回事,可以跳过例子说明,继续阅读下一个解法。但是可以尝试自己写一下代码,发现有些细节部分并不是那么容易写出来。
举个例子,假设有数据1,2,3,4,5,6:
从左往右扫描,找到第一个偶数2,并临时保存:
| 1 | 2 | 3 | 4 | 5 | 6 |
| ↑ | |||||
| 取出 |
将2后面的所有数往前移动一个位置,并将2放到最后一个位置:
| 1 | 3 | 4 | 5 | 6 | 2 |
| ↑ | 移动后 |
继续扫描当前位置,发现3为奇数,继续,发现4为偶数,将从3之后位置的数开始,到倒数第二个位置,所有数往前移动一个位置,并将4放到最后:
| 1 | 3 | 5 | 6 | 4 | 2 |
| ↑ | 移动后 |
继续扫描当前位置数5,6,至此,偶数有2两个,当前指向位置为,所在下标为4,总数 - 位置 <= 偶数 ,结束。
| 1 | 3 | 5 | 6 | 4 | 2 |
| ↑ |
根据该思路,C语言代码实现如下:
//reorder.c
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
void reorder(int arr[],int len)
{
if(NULL == arr || 0 == len)
return;
/*统计偶数数量,减少移动次数*/
int evenNum = 0;
int loop = 0;
int temp;
int inLoop = 0;
while(loop < len)
{
/*如果是偶数,则需要开始移动*/
temp = arr[loop];
/*如果已经达到了*/
if(0 == (temp & 1) && (len - loop > evenNum))
{
/*从当前位置开始移动,直到遇到剩下的数量是偶数的个数*/
for(inLoop = loop + 1;inLoop < len - evenNum;inLoop++)
{
arr[inLoop-1] = arr[inLoop];
}
/*把空出来的位填充上*/
arr[len - evenNum - 1] = temp;
evenNum++;
/*交换后继续*/
continue;
}
/*继续循环下一个*/
else
{
loop++;
}
}
}
解法二
很多人其实最先想到的解法可能是,创建一个新的数组,从头扫描,遇到偶数放后边,遇到奇数放前边。扫描结束后,再将数组内容拷贝到原数组,这样整个时间复杂度为(n),而空间复杂度也为O(n),这样的方法实现简单,也不容易出错。C语言代码实现如下:
//reorder1.c
void reorder(int arr[],int len)
{
if(NULL == arr || 0 == len)
return;
/*记录奇偶的数量*/
int oddNum = 0;
int evenNum = 0;
int loop = 0;
/*创建一个新的数组*/
int *temp = malloc(len * sizeof(int));
if(NULL == temp)
{
printf("malloc memory failed\n");
return;
}
/*拷贝数组,并遍历*/
memcpy(temp,arr,sizeof(int)*len);
for(;loop < len;loop++)
{
/*偶数放到数组末尾*/
if(0 == (temp[loop] & 1))
{
arr[len-1-evenNum] = temp[loop];
evenNum++;
}
/*奇数放到数组末尾*/
else
{
arr[oddNum] = temp[loop];
oddNum++;
}
}
free(temp);
}
解法三
还记得我们之前介绍过的《快速排序优化详解》吗?快速排序中,有一个分区操作,是将整个数组大于等于基准的部分放右边,而小于等于基准的部分放左边,即根据基准,将数组一分为二。其实在这里,同样可以参考这个思路,只不过跟基准比大小,变成了判断是奇还是偶。
这里简单描述一下该思路,更多细节可以参考《快速排序优化详解》中如何将元素移动到基准两侧一节:
- 定义下标i和j,分别从开头和结尾开始扫描
- 当i遇到偶数时,停止扫描
- 当j遇到奇数时,停止扫描
- 此时交换i和j位置的值
- 继续前面的操作,直到i和j交错或相等
举个例子,假设有数据1,2,3,4,5,6,7,8:
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| ↑ | ↑ | ||||||
| i | j |
i和j继续扫描,i遇到2停止,j遇到5停止,交换两处的值:
| 1 | 7 | 3 | 4 | 5 | 6 | 2 | 8 |
| ↑ | ↑ | ||||||
| i | j |
i和j继续扫描,i遇到4停止,j遇到5停止,交换两处的值:
| 1 | 7 | 3 | 5 | 4 | 6 | 2 | 8 |
| ↑ | ↑ | ||||||
| i | j |
继续扫描,此时,i和j交错,扫描结束:
| 1 | 7 | 3 | 5 | 4 | 6 | 2 | 8 |
| ↑ | ↑ | ||||||
| j | i |
基于该思路的算法时间复杂度为O(n),空间复杂度为O(1),C语言代码实现如下:
//reorder2.c
void reorder(int arr[],int len)
{
if(NULL == arr || 0 == len)
return;
int i = 0;
int j = len - 1;
int temp;
for(;;)
{
/*i j分别向右和向左移动,i遇到偶数停止,j遇到奇数停止?*/
while(1 == (arr[i] & 1))
{
i++;
}
while(0 == (arr[j] & 1))
{
j--;
}
if(i < j)
{
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
/*交错时停止*/
else
{
break;
}
}
}
运行效率比较
编译后,对一百万数据进行操作,运行时间结果如下。
解法一:
$ time ./reorder 1000000
并没有耐心等到结果出来。
解法二:
$ time ./reorder1 100000000
reorder for 100000000 numbers
before reorder:too much,will not print
after reorder:too much,will not print
real 0m2.425s
user 0m2.141s
sys 0m0.284s
对1亿数据进行操作,耗时很短,只是内存占用较多。
解法三:
$ time ./reorder2 100000000
reorder for 100000000 numbers
before reorder:too much,will not print
after reorder:too much,will not print
real 0m2.146s
user 0m2.018s
sys 0m0.128s
耗时很短,内存占用少。
微信公众号【编程珠玑】:专注但不限于分享计算机编程基础,Linux,C语言,C++,算法,数据库等编程相关[原创]技术文章,号内包含大量经典电子书和视频学习资源。欢迎一起交流学习,一起修炼计算机“内功”,知其然,更知其所以然。
扩展
在本题中,只是对整数是奇还是偶进行分开,那么如果是别的条件呢?例如是否为素数,是否为正数等等。我们可以让调用者传入一个条件函数,让它决定到底是放在后半部分,还是前半部分。这是不是很向库函数qsort需要传入一个比较函数的做法?这部分内容可以参考《函数指针》,根据这个思路,我们修改解法三的代码:
略
这个时候通过传入函数指针,可以对任意条件进行处理了。
总结
我们发现,一些基本算法的思想,通常可以用到其他问题上,而不同的思路,带来的效率可能天差地别。
练习
尝试自己实现上面的算法。如果需要保证奇数或偶数之间的相对位置不变,该如何修改?
备注
完整代码以及模拟一亿数据处理请访问:剑指offer:调整数组顺序使奇数位于偶数前面
微信公众号【编程珠玑】:专注但不限于分享计算机编程基础,Linux,C语言,C++,算法,数据库等编程相关[原创]技术文章,号内包含大量经典电子书和视频学习资源。欢迎一起交流学习,一起修炼计算机“内功”,知其然,更知其所以然。
剑指offer:调整数组顺序使奇数位于偶数前面的更多相关文章
- 剑指OFFER——调整数组顺序使奇数位于偶数前面
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变. 剑指offer书里的版本, ...
- 剑指Offer 调整数组顺序使奇数位于偶数前面
题目描述 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变. 思路: ...
- 用js刷剑指offer(调整数组顺序使奇数位于偶数前面)
题目描述 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变. 牛客网链接 js代码 ...
- 剑指offer--8.调整数组顺序使奇数位于偶数前面
习惯了简单 ------------------------------------------------- 时间限制:1秒 空间限制:32768K 热度指数:422906 本题知识点: 数组 题目 ...
- 剑指Offer-13.调整数组顺序使奇数位于偶数前面(C++/Java)
题目: 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变. 分析: 这道题做法有很 ...
- 剑指offer 调整数组顺序使得奇数位于偶数前面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class Solution { public: void ...
- 【Java】 剑指offer(21) 调整数组顺序使奇数位于偶数前面
本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集 题目 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇 ...
- 剑指Offer - 九度1516 - 调整数组顺序使奇数位于偶数前面
剑指Offer - 九度1516 - 调整数组顺序使奇数位于偶数前面2013-11-30 02:17 题目描述: 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部 ...
- 剑指Offer:调整数组顺序使奇数位于偶数前面【21】
剑指Offer:调整数组顺序使奇数位于偶数前面[21] 题目描述 输入一个整形数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分. 解题分析 使用插 ...
随机推荐
- 解决 win10飞行模式 无限自动开关 无法关闭
驱动问题,名为“Insyde Airplane Mode HID Mini-Driver”的驱动,这个驱动是专门用来快捷管理飞行模式的. 卸载完成后重启,无限开关飞行模式问题得到解决!
- Python:如何用一行代码获取上个月是几月
现在转一篇志军100发于公众号 Python之禅的文章: Python:如何用一行代码获取上个月是几月 抱歉我用了个有点标题党的标题,因为担心你错过了本文,但内容绝对干货,本文介绍的关于Python时 ...
- 前端技术大行其道,再不拥抱TypeScript你就老了!
本篇文章旨在介绍下TypeScript这门新兴的程序设计语言,不需要你有多么强的专业知识,只需要你有半支烟的功夫. 好了,废话不多说,赶紧进入正题.为什么你需要拥抱TypeScript?那么你首先需要 ...
- RocketMQ4.4.0新特性分享
rocketmq1.架构 MQ历史 由数据结构队列发展而来 MQ使用场景 异步处理 解耦 削峰填谷 数据同步2.队列3.使用 生产 同步(sync) 默认重试2次总共3次 默认等待超时时间为3s 异步 ...
- 将Python 程序打包成 .exe格式入门
PyInstaller PyInstaller 是一个十分有用的第三方库,可以用来打包 python 应用程序,打包完的程序就可以在没有安装 Python 解释器的机器上运行了. 它能够在 Windo ...
- java SE Development kit 8 Update 201 (64-bit) 的安装相关
登录http://www.oracle.com,下载JDK(J2SE) JDK 1.0,1.1,1.2,1.3,1.4 1.5 (JDK5.0) à支持注解.支持泛型 1.6(JDK6.0) à ...
- 【深度学习篇】--Seq2Seq模型从初识到应用
一.前述 架构: 问题: 1.压缩会损失信息 2.长度会影响准确率 解决办法: Attention机制:聚焦模式 “高分辨率”聚焦在图片的某个特定区域并以“低分辨率”,感知图像的周边区域的模式.通过大 ...
- .NET Core实战项目之CMS 第一章 入门篇-开篇及总体规划
作者:依乐祝 原文地址:https://www.cnblogs.com/yilezhu/p/9977862.html 写在前面 千呼万唤始出来,首先,请允许我长吸一口气!真没想到一份来自28岁老程序员 ...
- JSP 内置对象(上)
JSP 内置对象是 Web 容器创建的一组对象,不使用 new 关键字就可以直接使用的对象.如上一章中使用脚本实现打印九九乘法表中的out对象 <%-- 脚本:out对象是JSPWriter类的 ...
- 多线程协作wait、notify、notifyAll方法简介理解使用 多线程中篇(十四)
在锁与监视器中有对wait和notify以及notifyAll进行了简单介绍 所有对象都有一个与之关联的锁与监视器 wait和notify以及notifyAll之所以是Object的方法就是因为任何一 ...
