剑指offer 题21—调整数组顺序是奇书位于偶数前面

2023-10-08 14:32

本文主要是介绍剑指offer 题21—调整数组顺序是奇书位于偶数前面,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

调整数组顺序是奇书位于偶数前面

题目

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

初步分析

最简单的办法当然是从头扫描这个数组,每碰到一个偶数,就该数放到最后面,也就是这个数后面的数都要向前移动。
由于每碰到一个偶数就要移动O(n)个数字,总的时间复杂度为O(n^2)。

更好的办法

既然数组中只有两类数字——非奇即偶。那么每当发现有偶数出现在奇数前面时,就交换它们的顺序,这样岂不是可以了?

因此,需要维护两个指针:
第一个指针,初始化时指向第一个数字,只能向后移动。
第二个指针,初始化时指向最后一个数字,只能向前移动。

两指针相遇之前,如果第一个指针是偶数,而第二个指针是奇数,那么就将它们的位置交换

举例

输入数组:{1,2,3,4,5}

过程:
1、开始,第一个指针指向1,第二个指针指向5
2、由于1是奇数,因此第一个指针向后移动,直到指向偶数2。
3、此时,因为第二个指针指向5,因此交换,并且两者都进行移动。
4、然后继续直到两者相遇

图解:
在这里插入图片描述
因此可以得到以下代码:

void ReorderOddEven(int* pData,unsigned int length)
{if(pData==nullptr || length==0)return;int* pBegin=pData;int* pEnd=pData+length+1;while(pBegin<pEnd){//向后移动pBegin 直到它指向偶数while(pBegin<pEnd && (*pBegin & 0x1)!=0)pBegin++;//向前移动pEnd 直到指向奇数while(pBegin<pEnd && (*pEnd & 0x1)==0)pEnd--;if(pBegin<pEnd){int temp=*pBegin;*pBegin=*pEnd;*pEnd=tmp;}}
}

拓展——模块化的处理

上面的问题解决了,似乎也很完美,但是如果又有类似得问题如何?

问:如果改成数组分为正负两部分,负数在非负数前面,怎么处理?
答:重新定义一个新函数,只需要更改第二个和第三个while循环得判断条件就好了

再问:那如果又是分成两部分,一部分能被3整除,放前面,一部分不能被3整除,放后面,又怎么办呢?
再答:还是重新定义一个新函数,再更改… 打断:停停停,就没有更好得办法了?

我们看上面得问题,其实回答并没有错,因此就是只需要改一下条件就好了。
但上面错就错在这个只需要上面了

既然只是判断条件有需要改,那么我何必要每次都重复写其它一样的代码呢?
为何不将需要判断的条件封装起来,这样剩下的代码不就可以重用了?
没错,这正式提问者所想得到的答案!

因此我们只需要引入一个函数指针,将上面代码拆分成逻辑和运算两部分就好了。这样每次只需要重写逻辑函数,而不必重复一大段运算了,重用性提高了很多

void ReorderOddEven(int* pData,unsigned int length,bool (*func)(int))
{if(pData==nullptr || length==0)return;int* pBegin=pData;int* pEnd=pData+length+1;while(pBegin<pEnd){//向后移动pBegin 直到它指向偶数while(pBegin<pEnd && !func(*pBegin))pBegin++;//向前移动pEnd 直到指向奇数while(pBegin<pEnd && func(*pEnd) )pEnd--;if(pBegin<pEnd){int temp=*pBegin;*pBegin=*pEnd;*pEnd=tmp;}}
}bool IsEven(int n)
{return (n&0x1)==0;
}

————————————————————————————————————————————————————————
参考书籍:《剑指offer》

这篇关于剑指offer 题21—调整数组顺序是奇书位于偶数前面的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/165959

相关文章

Java数组动态扩容的实现示例

《Java数组动态扩容的实现示例》本文主要介绍了Java数组动态扩容的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录1 问题2 方法3 结语1 问题实现动态的给数组添加元素效果,实现对数组扩容,原始数组使用静态分配

Java中ArrayList与顺序表示例详解

《Java中ArrayList与顺序表示例详解》顺序表是在计算机内存中以数组的形式保存的线性表,是指用一组地址连续的存储单元依次存储数据元素的线性结构,:本文主要介绍Java中ArrayList与... 目录前言一、Java集合框架核心接口与分类ArrayList二、顺序表数据结构中的顺序表三、常用代码手动

JavaScript对象转数组的三种方法实现

《JavaScript对象转数组的三种方法实现》本文介绍了在JavaScript中将对象转换为数组的三种实用方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友... 目录方法1:使用Object.keys()和Array.map()方法2:使用Object.entr

JAVA实现亿级千万级数据顺序导出的示例代码

《JAVA实现亿级千万级数据顺序导出的示例代码》本文主要介绍了JAVA实现亿级千万级数据顺序导出的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 前提:主要考虑控制内存占用空间,避免出现同时导出,导致主程序OOM问题。实现思路:A.启用线程池

JavaScript中比较两个数组是否有相同元素(交集)的三种常用方法

《JavaScript中比较两个数组是否有相同元素(交集)的三种常用方法》:本文主要介绍JavaScript中比较两个数组是否有相同元素(交集)的三种常用方法,每种方法结合实例代码给大家介绍的非常... 目录引言:为什么"相等"判断如此重要?方法1:使用some()+includes()(适合小数组)方法2

python 线程池顺序执行的方法实现

《python线程池顺序执行的方法实现》在Python中,线程池默认是并发执行任务的,但若需要实现任务的顺序执行,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋... 目录方案一:强制单线程(伪顺序执行)方案二:按提交顺序获取结果方案三:任务间依赖控制方案四:队列顺序消

Java中数组与栈和堆之间的关系说明

《Java中数组与栈和堆之间的关系说明》文章讲解了Java数组的初始化方式、内存存储机制、引用传递特性及遍历、排序、拷贝技巧,强调引用数据类型方法调用时形参可能修改实参,但需注意引用指向单一对象的特性... 目录Java中数组与栈和堆的关系遍历数组接下来是一些编程小技巧总结Java中数组与栈和堆的关系关于

Spring Bean初始化及@PostConstruc执行顺序示例详解

《SpringBean初始化及@PostConstruc执行顺序示例详解》本文给大家介绍SpringBean初始化及@PostConstruc执行顺序,本文通过实例代码给大家介绍的非常详细,对大家的... 目录1. Bean初始化执行顺序2. 成员变量初始化顺序2.1 普通Java类(非Spring环境)(

浅析Spring如何控制Bean的加载顺序

《浅析Spring如何控制Bean的加载顺序》在大多数情况下,我们不需要手动控制Bean的加载顺序,因为Spring的IoC容器足够智能,但在某些特殊场景下,这种隐式的依赖关系可能不存在,下面我们就来... 目录核心原则:依赖驱动加载手动控制 Bean 加载顺序的方法方法 1:使用@DependsOn(最直

Java中的数组与集合基本用法详解

《Java中的数组与集合基本用法详解》本文介绍了Java数组和集合框架的基础知识,数组部分涵盖了一维、二维及多维数组的声明、初始化、访问与遍历方法,以及Arrays类的常用操作,对Java数组与集合相... 目录一、Java数组基础1.1 数组结构概述1.2 一维数组1.2.1 声明与初始化1.2.2 访问