一道不简单又简单的Google面试题

早上醒来,拿着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


后来我总结了下,解题的时候一定要多动手,我以前总是只动脑子,实际上多写写你就会发现解题关键点。这道题正是我趟床上没想出来,下床后写了几步,五分钟就找到解法了。所以多写写总会有好处的。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值