今天给你们带来的是 《剑指 Offer》习题:调整数组顺序使奇数位于偶数前面,纯 Java 实现但愿你们多加思考。java
面试题:输入一个整型数组,实现一个函数来调整该数组中的数字的顺序,使得全部奇数位于数组的前半部分,全部偶数位于数组的后半部分,但愿时间复杂度尽可能小。程序员
看到这道题,想必大多数人都是能一下就想到从头至尾扫描一遍数组,而后遇到奇数就移动到最前面,遇到偶数就移动到最后面的思路,因而便有了下面的代码。面试
注:《剑指 Offer》上面的 「遇到奇数移动到最前面,遇到偶数也移动到最后面」其实只须要作其中一种便可。算法
public class Test14 { private static int[] reOrderArray(int[] arr) { for (int i = 0; i < arr.length; i++) { // 遇到奇数就放到最前面 if (Math.abs(arr[i]) % 2 == 1) { int temp = arr[i]; // 先把 i 前面的都向后移动一个位置 for (int j = i; j > 0; j--) { arr[j] = arr[j - 1]; } arr[0] = temp; } } return arr; } public static void main(String[] args) { int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9}; arr = reOrderArray(arr); for (int i = 0; i < arr.length; i++) { System.out.print(arr[i] + " "); } System.out.println(); int[] arr1 = {2, 4, 6, 8, 1, 3, 5, 7, 9}; arr1 = reOrderArray(arr1); for (int i = 0; i < arr1.length; i++) { System.out.print(arr1[i] + " "); } System.out.println(); int[] arr2 = {2, 4, 6, 8, 10}; arr2 = reOrderArray(arr2); for (int i = 0; i < arr2.length; i++) { System.out.print(arr2[i] + " "); } } }
上面的代码当然能达到功能,但时间复杂度上彻底不能恭维。每找到一个奇数,咱们老是要去移动很多个位置的数。设计模式
等等。数组
咱们上面算法最大的问题在于移动,咱们可否不作这个移动呢?ide
固然是能够的。题目要求全部奇数都应该在偶数前面,因此咱们应该只须要维护两个下标值,让一个下标值从前日后遍历,另一个下标值从后往前遍历,当发现第一个下标值对应到偶数,第二个下标值对应到奇数的时候,咱们就直接对调两个值。直到第一个下标到了第二个下标的后面的时候退出循环。函数
咱们有了这样的想法,能够先拿一个例子在心中走一遍,若是没有问题再写代码,这样也可让面试官知道,咱们并非那种上来就开始写代码不考虑全面的程序员。spa
心中默走一遍没问题后,开始手写代码:设计
public class Test14 { private static int[] reOrderArray(int[] arr) { int odd = 0, even = arr.length - 1; // 循环结束条件为 odd >= even while (odd < even) { // 第一个下标为偶数的时候中止 while (odd < even && Math.abs(arr[odd]) % 2 != 0) { odd++; } // 第二个下标为奇数的时候中止 while (odd < even && Math.abs(arr[even]) % 2 == 0) { even--; } // 找到后对调两个值 int temp = arr[odd]; arr[odd] = arr[even]; arr[even] = temp; } return arr; } public static void main(String[] args) { int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9}; arr = reOrderArray(arr); for (int i = 0; i < arr.length; i++) { System.out.print(arr[i] + " "); } System.out.println(); int[] arr1 = {2, 4, 6, 8, 1, 3, 5, 7, 9}; arr1 = reOrderArray(arr1); for (int i = 0; i < arr1.length; i++) { System.out.print(arr1[i] + " "); } System.out.println(); int[] arr2 = {2, 4, 6, 8, 10}; arr2 = reOrderArray(arr2); for (int i = 0; i < arr2.length; i++) { System.out.print(arr2[i] + " "); } System.out.println(); } }
若是是面试应届毕业生或者工做时间不长的程序员,面试官可能会满意前面的代码,但若是应聘者申请的是资深 的开发岗位,那面试官可能会接着问几个问题。
- 面试官:若是把题目改为把数组中的数组按照大小分为两部分,全部的负数都在非负整数的前面,该怎么作?
- 应聘者:这很简单,能够从新定义一个函数,在新的函数里,只要修改第二个和第三个 while 循环里面的判断条件就行了。
- 面试官:若是再把题目改改,变成把数组中的数分为两部分,能被 3 整除的数都在不能被 3 整除的数的前面,怎么办?
- 应聘者:咱们仍是能够定义一个新的函数,在这个函数中......
- 面试官:(打断应聘者的话),难道就没有更好的方法?
这个时候应聘者应该要反应过来,面试官期待咱们能提供的不只仅是解决一个问题的办法,而是解决一系列同类型问题的通用方法。咱们在作解法的时候不能只想着解决当前的问题就好。在《大话设计模式》中,讲解了一个很是有意思的事情就是大鸟让小菜作商场促销活动的时候,各类改变需求,把小菜绕的云里雾里。
《大话设计模式》PDF 版本能够在公众号后台回复「大话设计模式」便可获取。
是呀,哪有不变的需求,需求不变,咱们哪来那么多活干呀?不过要是,咱们事先就作了这样的准备,省下来的时间那不是正好又能够去玩一盘吃鸡洛?
回到面试官新提出的两个问题来,咱们其实新的函数都只须要更改第二个和第三个 while 循环里面的判断条件,而其它都是不须要动的。
public class Test14 { interface ICheck { boolean function(int n); } public static class OrderEven implements ICheck { @Override public boolean function(int n) { return n % 2 == 0; } } private static int[] reOrderArray(int[] arr, ICheck iCheck) { int odd = 0, even = arr.length - 1; // 循环结束条件为 odd >= even while (odd < even) { // 第一个下标为偶数的时候中止 while (odd < even && !iCheck.function(arr[odd])) { odd++; } // 第二个下标为奇数的时候中止 while (odd < even && iCheck.function(arr[even])) { even--; } // 找到后对调两个值 int temp = arr[odd]; arr[odd] = arr[even]; arr[even] = temp; } return arr; } public static void main(String[] args) { OrderEven even = new OrderEven(); int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9}; arr = reOrderArray(arr,even); for (int i = 0; i < arr.length; i++) { System.out.print(arr[i] + " "); } System.out.println(); int[] arr1 = {2, 4, 6, 8, 1, 3, 5, 7, 9}; arr1 = reOrderArray(arr1,even); for (int i = 0; i < arr1.length; i++) { System.out.print(arr1[i] + " "); } System.out.println(); int[] arr2 = {2, 4, 6, 8, 10}; arr2 = reOrderArray(arr2,even); for (int i = 0; i < arr2.length; i++) { System.out.print(arr2[i] + " "); } System.out.println(); } }
写这玩意儿的时候,我心里是拒绝的,因为 Java 没有 Python 同样方便的函数指针,我想了想只想到了用接口方式来处理。要是有其余实现方式的但愿你们能在评论区留言~
好了,今天的面试讲解,就先到这儿吧。