网易2018校招内推Android笔试编程题一道

题目:
输入一个数组,长度不超过2*10^5,数组元素为整数,大小不超过10^9,从第一个位置依次取元素并添加到新数组末尾,每取一次就逆序操作一次,直到取完所有元素。最后输出这个逆序操作多次后的新数组。
示例:
原数组:1 2 3 4
取1,逆序后还是1
取2后数组为1 2,逆序后是2 1
取3后数组为2 1 3,逆序后是3 1 2
取4后数组为3 1 2 4,逆序后是4 2 1 3
要求:
输入第一行为n,表示数组长度;第二行为数组,以空格隔开。
输出结果数组,以空格隔开,最后不能有空格。

思路:
平时还是做这种算法题太少,3道大题拿到题后就这个稍微有点思路。一开始被题目误导,写了个逆序函数去操作,每取一个数就调用一次函数,结果时间复杂度太高,TimeOut,无法AC。
所以一开始我的代码是这样的:

import java.util.Scanner;

public class Test {

    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        while (scan.hasNext()) {
            int n = scan.nextInt();
            int a[] = new int[n];
            for (int i = 0; i < n; i++) {
                a[i] = scan.nextInt();
                // 每读入了新元素就逆序操作一次,这样时间复杂度基本上就是O(n^2)
                reverse(a, i + 1);
            }
            for (int i = 0; i < n; i++) {
                if (i == n - 1) {
                    System.out.print(a[i]);
                } else {
                    System.out.print(a[i] + " ");
                }
            }
        }
    }

    private static void reverse(int a[], int len) {
        if (len > 1) {
            int temp;
            for (int i = 0; i < len / 2; i++) {
                temp = a[i];
                a[i] = a[len - i - 1];
                a[len - i - 1] = temp;
            }
        }
    }
}

其实通过观察几个结果可以知道,这个根本不需要遍历地去逆序操作,而是根据规律去添加元素:
1 2 3 4 → 4 2 1 3
1 2 3 4 5 → 5 3 1 2 4
1 2 3 4 5 6 → 6 4 2 1 3 5
从数组末尾开始,取两个数,分别放在左右两端,再取两个数,放在内部的两端,依次类推,所以中间那个数当长度为奇数时永远都是1,为偶数时就是2,1。
生成过程:
1 2 3 4 5
5 _ _ _ 4
5 3 _ 2 4
5 3 1 2 4

AC代码:

import java.util.Scanner;

public class Test {

    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        while (scan.hasNext()) {
            int n = scan.nextInt();
            int a[] = new int[n];
            for (int i = 0; i < n; i++) {
                a[i] = scan.nextInt();
            }
            int b[] = reverseBest(a, n);
            for (int i = 0; i < n; i++) {
                if (i == n - 1) {
                    System.out.print(b[i]);
                } else {
                    System.out.print(b[i] + " ");
                }
            }
        }
    }

    // 时间复杂度降低为O(n)
    private static int[] reverseBest(int a[], int len) {
        int b[] = new int[len]; // 搞一个空白数组来填
        if (len > 1) {
            for (int i = 0, j = 0; i < len / 2; i++) {
                // 如i=0时左端元素就来自原数组倒数第一个
                b[i] = a[len - 1 - j]; 
                // 右端元素来自原数组的倒数第二个
                b[len - 1 - i] = a[len - 2 - j];
                j += 2; // 从后往前取,每次取两个
            }
            if (len % 2 != 0) { // 最后如果是奇数列,要把中间的元素补1,因为空数组默认元素都是0
                b[len / 2] = 1;
            }
            return b;
        } else {
            return a; // len=1时,直接返回原数组,无需逆序操作
        }
    }
}

唉,总的来说还是学艺不精,算法编程题还是要多见多刷,才会很快就有思路。
说来是Android的笔试题,其实真正的Android知识题目只有三四个选择题(UI、SQL语句、内存泄露等),加上两三个Java的题(主要是线程方面的知识,wait、notify、sleep之类的),其余选择题都是计算机的专业知识包括网络、操作系统(Linux的一些命令操作),而且考察的形式基本上是“下列哪个说法是错误的”,也就是说有3个都是正确的。
校招还是更注重基础知识

点赞