除自身以外数组的乘积

题目链接https://leetcode-cn.com/problems/product-of-array-except-self/

一、问题描述

给你一个长度为 n 的整数数组 nums,其中 n > 1,返回输出数组 output ,其中 output[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积。

 

示例:

输入: [1,2,3,4]
输出: [24,12,8,6]
 

提示:题目数据保证数组之中任意元素的全部前缀元素和后缀(甚至是整个数组)的乘积都在 32 位整数范围内。

说明: 请不要使用除法,且在 O(n) 时间复杂度内完成此题。

进阶:
你可以在常数空间复杂度内完成这个题目吗?( 出于对空间复杂度分析的目的,输出数组不被视为额外空间。)

 

二、问题分析

  首先题意为将一个数组按[i]位置划分开,计算左边和右边所有元素的乘积,继续将问题简化成两个子问题就是,计算[i]左侧所有元素的乘积;计算[i]右侧所有元素的乘积,然后将这两个子问题的解合并就得出该问题的解了。

  这个解法的思考过程就是列式子,然后分析每个式子的特点即可。

  可以定义两个数组分别保存[i]左侧元素的乘积和右侧元素的乘积,只需要两个数组和常数次的遍历就可以实现。

  接下来按进阶的要求对代码进行简化,根据题目的提示,可以在输出数组上对结果进行操作,首先分析上文中构造两个辅助数组的过程,①构造左乘积数组L,其实就是从左往右对数组元素进行累乘,将每步结果填入数组,②对于右乘积数组R其实就是从右往左对数组元素进行累乘,并将结果填入数组;③在最后对左乘积数组和右乘积数组对应位置的元素进行相乘就得出了结果数组。

  上面三步中最后一步显然可以在数组L或数组R中的任意一个的基础上实现,这是否暗示整个计算过程都可以在一个数组上实现?其实是可以的,首先可以构造数组L,这个数组最后将作为结果数组进行输出。对于步骤②和③,其实从右向左遍历的过程可以和最后求值的过程结合,我们只需要在逆序遍历输入数组的同时逆序遍历数组L,而且对L中的值进行计算即可。具体实现在代码中体现。

 

三、算法实现

  代码:

 1 class Solution {
 2     public int[] productExceptSelf(int[] nums) {
 3         int n = nums.length;
 4         int[] output =  new int[n];
 5         //因为第一个元素左侧无元素所以左侧所有元素的乘积定义为1
 6         output[0] = 1;
 7         for(int i=0;i<n-1;i++){
 8             output[i+1] = nums[i]*output[i];
 9         }
10         //接下来从右向左构造结果数组
11         int r = 1;//用r来保存每步的乘积,用于后续的累乘
12         for(int i=n-1;i>0;i--){
13             //因为对于最后一个元素,要求的结果数组就是左边全部元素的积,所以直接从到数第二个元素开始
14             r = r*nums[i];
15             output[i-1] = output[i-1]*r;
16         }
17         return output;
18     }
19 }