这个题目就是《剑指offer》上面的面试题66。刚开始就是按照上面的思路然后构造了两个数组pre和last,分别记录从前往后的累乘积和从后往前的累乘积,然后再遍历一次得到result,其中result[i] = pre[i-1] * last[i+1]
。代码如下:
public static int[] productExceptSelf(int[] nums) {
int numLength = nums.length;
int[] pre = new int[numLength];
pre[0] = nums[0];
int[] last = new int[numLength];
last[numLength-1] = nums[numLength-1];
//开始构造pre和last两个数组
for (int i = 1, j = numLength-2; i < numLength && j >= 0; i++, j--) {
pre[i] = pre[i-1] * nums[i];
last[j] = last[j+1] * nums[j];
}
int[] result = new int[numLength];
//得到result数组
for (int i = 1; i < numLength-1; ++i) {
result[i] = pre[i-1] * last[i+1];
}
//两端的值单独处理
result[0] = last[1];
result[numLength-1] = pre[numLength-2];
return result;
}
当然这样做的时间复杂度是O(2n)(或者O(n)),空间复杂度为O(2n),result不算在里面。题目说能否使得空间复杂度为常量,思考一番还是无果,于是看了其他人的实现,也是看了好一会才懂。先看一下代码:
public static int[] productExceptSelf1(int[] nums) {
int numLength = nums.length;
int[] result = new int[numLength];
for (int i = 0; i < numLength; ++i)
result[i] = 1;
int left = 1, right = 1;
//一个循环就搞定了
for (int i = 0; i < numLength; ++i) {
result[i] *= left;
left *= nums[i];
result[numLength-1-i] *= right;
right *= nums[numLength-1-i];
}
return result;
}
主要是看看后面那个for循环,这里很巧妙只遍历了一次就计算出了所有结果。我们将这个步骤分为两步:
- 首先分别计算左边的累乘结果和右边的累乘结果,直到左右指针相遇时。
- 然后右边的指针就可以直接利用左边的累乘结果更新左边的结果,左边的指针也可以利用右边的累乘结果更新右边的结果。
上一个图就能看的比较清晰。要知道left和right的更新只和nums有关,而result的更新则只与left、right都有关,绿色的线表示这一步要进行的乘法操作。