早上醒来,拿着ipad随便找了点题提提神,正好发现了一道google面试的算法题。题目如下:
“给你一个数组A[0..n],请你在O(n)的时间里构造一个新的数组B[0..n],使得B[i]=A[0]*A[1]*A[2]*…*A[n]/A[i],但是不能使用除法运算。”
这道题看似不好做,实际上换个角度考虑是很简单的。有的人一分钟就想出来了,有的人要一个小时,而且想了很久去看答案的你肯定会抓狂:P
题目的重点(不能说是难点)在于不能使用除法,而恰巧这一点是计算机的思维,为什么会这么说?因为如果叫你用笔算,你会先全部乘起来再除以A[i]么?肯定不会,你绝对会把A[i]消掉。
举个例子来说。比如A有10个数,我们现在要算B[5],在纸上演算的过程就一定是 A[0]*A[1]*A[2]*A[3]*A[4] * A[6]*A[7]*A[8]*A[9]
这个时候可以发现,整个计算过程就是先算A[0~4]的乘积,再算A[6~9]的乘积,然后两者再乘起来。So,答案浮出水面,只要针对每一个下标,算出前后两部分的乘积,则该下标对应的值就是这两部分乘积的乘积。不过这种做法是典型的“空间换时间”
测试代码如下,可以参考下:
import java.util.Random;
/**
*
* 给你一个数组A[0..n],请你在O(n)的时间里构造一个新的数组B[0..n],使得B[i]=A[0]*A[1]*A[2]*…*A[n]/A[i],但是不能使用除法运算。
*
* Created by Luonanqin on 12/1/14.
*/
public class ArrayA2ArrayB {
public static int[] buildArrayB(int[] arrayA) {
int len = arrayA.length;
int[] order = new int[len];
int[] invertedOrder = new int[len];
order[0] = 1;
invertedOrder[len - 1] = 1;
// 计算两部分乘积只需要一个循环就可以搞定,虽然说两个单独循环不影响大O,但是当然时间越节省越好
for (int i = 1; i < len; i++) {
// 计算从前到后依次的乘积,即
// order[1] = order[0] * A[1]
// order[2] = order[0] * A[1] * A[2] = order[1] * A[2]
// 直到order[n]=order[n-2]*A[n-1]
order[i] = arrayA[i - 1] * order[i - 1];
// 计算从后到前的乘积,即
// invertedOrder[n-1] = invertedOrder[n] * A[n]
// invertedOrder[n-2] = invertedOrder[n] * A[n] * A[n-1] = invertedOrder[n-1] * A[n-1]
// 直到invertedOrder[0] = invertedOrder[1] * A[1]
invertedOrder[len - i - 1] = arrayA[len - i] * invertedOrder[len - i];
}
int[] arrayB = new int[arrayA.length];
for (int i = 0; i < arrayB.length; i++) {
arrayB[i] = order[i] * invertedOrder[i];
}
return arrayB;
}
public static void main(String[] args) {
int[] arrayA = new int[10];
Random r = new Random(System.currentTimeMillis());
for (int i = 0; i < arrayA.length; i++) {
int num = 0;
// 随机产生小于10的数值,但不能为0
while ((num = r.nextInt(10)) == 0)
;
arrayA[i] = num;
}
// 打印数组A
System.out.print("Array A: ");
for (int i = 0; i < arrayA.length; i++) {
System.out.print(arrayA[i]);
if (i < arrayA.length - 1) {
System.out.print(", ");
}
}
System.out.println();
// 打印数组B
int[] arrayB = buildArrayB(arrayA);
System.out.print("Array B: ");
for (int i = 0; i < arrayB.length; i++) {
System.out.print(arrayB[i]);
if (i < arrayB.length - 1) {
System.out.print(", ");
}
}
}
}
随机的执行结果:
Array A: 5, 4, 2, 1, 8, 7, 1, 3, 1, 1
Array B: 1344, 1680, 3360, 6720, 840, 960, 6720, 2240, 6720, 6720
后来我总结了下,解题的时候一定要多动手,我以前总是只动脑子,实际上多写写你就会发现解题关键点。这道题正是我趟床上没想出来,下床后写了几步,五分钟就找到解法了。所以多写写总会有好处的。