面试:扩展性好的代码,更容易获得Offer

面试题:输入一个整型数组,实现一个函数来调整该数组中的数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分,希望时间复杂度尽量小。

看到这道题,相比大多数人都是能一下就想到从头到尾扫描一遍数组,然后遇到奇数就移动到最前边,遇到偶数就移动到最后边的思路。于是便有了下面的代码。

注:《剑指Offer》上面的【遇到奇数移动到最前面,遇到偶数移动到最后边】其实只需要做其中一种即可。


/**
 * Describe:
 * <p>
 * Author: Lixm
 * Date: 2018/9/5
 */
public class HeadOddNumberDemo {

    private static int[] reOrderArray(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            //遇到奇书就放到最前边
            if ((Math.abs(arr[i]) & 0x1) == 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<args.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] + " ");
        }
    }

}

运行结果:

9 7 5 3 1 2 4 6 8 
9 7 5 3 1 2 4 6 8 
2 4 6 8 10 

以上代码固然能达到功能,但时间复杂度上完全不敢恭维。没找到一个奇数,我们总是要移动到不少个位置的数。

等等。

我们上面的算法最大的问题在于移动,我们能否不做这个移动呢?

当然是可以的。题目要求所有奇数都应该在偶数前面,所以我们应该只需要维护两个下标值,让一个下标值从前往后遍历,另外一个下标值从后往前遍历,当发现第一个下标值对应到偶数,第二个下标值对应到奇数的时候,我们就直接对调两个值,直到第一个下标到了第二个下标的后边的时候,退出循环

我们有了这样的想法,可以先拿一个例子在心中走一遍,如果没有问题在写代码,这样可以让面试官知道,我们并不是那种上来就开始写代码不考虑周全的程序员。

1、假定输入的数组是{1,2,3,4,5}

2、设定odd=0,代表第一个下标;even=arr.length=4;

3、从前往后移动第一个下标odd,直到它等于偶数,即当odd=1的时候,我们停止移动。

4、在从后往前移动下标even,直到它等于奇数,即当even=4的时候,我们停止移动;

5、满足arr[odd]为偶数,arr[even]为奇数,我们对调两个值,得到新数组{1,5,3,4,2}

6、继续循环,此时odd=3,even=2,不满足odd<even条件,退出循环,得到的数组符合条件;

心中默走一遍,没有问题后,开始手写代码:

 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]) & 0x1) == 1) {
                odd++;
            }
            //第二个下标为奇数的时候停止
            while (odd < even && (Math.abs(arr[even]) & 0x1) != 1) {
                even--;
            }
            //找到后对调两个值
            int temp = arr[odd];
            arr[odd] = arr[even];
            arr[even] = temp;
        }
        return arr;
    }

扩展性更好的代码,能秒杀Offer

如果是面试应届毕业生或者工作时间不长的程序员,面试官可能会满意前面的代码,但是如果应聘者申请的是资深的开发岗位,那面试官接下来可能接着问几个问题。

  • 面试官:如果把题目改成把数组中的数组按照大小分为两部分,所有的负数都在非负整数的前面,该怎么做?
  • 应聘者:这很简单,可以重新定义一个函数,在新的函数里,只要修改第二个和第三个while循环里面的判断条件就好了。
  • 面试官:如果把题目在改改,变成把数组中的数分为两部分,能被3整除的数都在不能被3整除的数的前面,怎么办?
  • 应聘者:我们还可以定一个一个新的函数,在这个函数中。。。
  • 面试官:(打断应聘者的话),难道就没有更好的方法?

这个时候应聘着应该要反应过来,面试官期待我们能提供的不仅仅是解决一个问题的办法,而是解决一系列同类问题的通用方法。我们在做解法的时候不能只想着解决当前的问题就好。在《大话设计模式》中,讲解了一个非常有意思的事情,就是大鸟让小菜做商场促销活动的时候,各种改变需求,把小菜绕得云里雾里。

是呀,哪有不变的需求,需求不变,我们哪来那么多活干呀?不过要是,我们事先就做了这样的准备,省下来的时间那不是正好有可以玩一盘吃鸡喽?

回到面试官新提出的两个问题来,我们其实新的函数都只需要更改第二个和第三个while循环里面的判断条件,而其他都不需要改动。

package com.lixm.animationdemo.Other;

/**
 * Describe:
 * <p>
 * Author: Lixm
 * Date: 2018/9/5
 */
public class HeadOddNumberDemo {

//    private static int[] reOrderArray(int[] arr) {
//        for (int i = 0; i < arr.length; i++) {
//            //遇到奇书就放到最前边
//            if ((Math.abs(arr[i]) & 0x1) == 1) {
//                int temp = arr[i];
//                //先把i前面的都向后移动一个位置
//                for (int j = i; j > 0; j--) {
//                    arr[j] = arr[j - 1];
//                }
//                arr[0] = temp;
//            }
//        }
//        return arr;
//    }

    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] + " ");
        }
    }

}

运行结果:

1 9 3 7 5 6 4 8 2 
9 7 5 3 1 8 6 4 2 
2 4 6 8 10 

写这玩意儿的时候,我内心是拒绝的,由于 Java 没有 Python 一样方便的函数指针,我想了想只想到了用接口方式来处理。要是有其他实现方式的希望大家能在评论区留言~

本文摘自 文章地址 感谢nanchen的分享

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值