4-238. Product of Array Except Self

题目描述:

Given an array nums of n integers where n > 1,  return an array output such that output[i] is equal to the product of all the elements of nums except nums[i].

Example:

Input:  [1,2,3,4]
Output: [24,12,8,6]

Note: Please solve it without division and in O(n).

Follow up:
Could you solve it with constant space complexity? (The output array does not count as extra space for the purpose of space complexity analysis.)

代码实现(个人版):

 1 class Solution:
 2     def productExceptSelf(self, nums):
 3         length = len(nums)
 4         left, right = 1, 1
 5         m, n = 0, 0
 6         aux = []
 7         while m < length:
 8             aux.append(right)
 9             right *= nums[-m-1]
10             m += 1
11             
12         while n < length:
13             aux[-n-1] = left * aux[-n-1]
14             left *= nums[n]
15             n += 1
16         
17         return aux[::-1] 
18 
19 if __name__ == '__main__':
20     x = [1, 2, 3, 4]
21     y = Solution().productExceptSelf(x)
22     print(y)

分析(个人版):

题目要求:不能使用除法;必须在O(n)时间内完成;在常数空间复杂度的范围内完成且输出序列不占用额外的空间复杂度。

基于以上考虑,得到以下思路:

(1)首先不能做重复的乘法运算,否则时间复杂度必然不能满足;

(2)题目的特点是以nums[i]为界将其分为左右两部分的,因此可以考虑“分而治之”:定义一个左累乘器left和右累乘器right,指针i从左向右依次滑过nums的每个位置,left随着i的滑动依次乘以nums中的下一个元素。right是left的镜像,相当于随着虚拟指针j(实际并不存在)从右向左滑动时依次乘以从右向左的下一个元素。这样当指针i和j滑遍整个nums数组时,就得到了output数组中所有位置需要的所有因式项,而且大大减少了重复乘法运算的次数;

(3)对于空间复杂度的考量:由于输出序列不占用额外的空间复杂度,因此不妨让输出序列(即代码中的aux列表)充当额外需要的空间,这样就可以满足空间复杂度的要求;

(4)在代码中的第二个while循环结束之后,aux中保存的结果是反过来的(这是出于对尽量节省空间的考虑,尽量只使用aux这一个额外的空间),因此最终return时要再对其进行逆序。

由list引发的runtime 和 memory cost 的一点分析:

上述个人版的runtime 和 memory cost 为:

如果将上述代码的第6-8行改为以下方式而其他地方不变:

1 aux = [0] * length
2 while m < length:
3       aux[m] = right

则runtime 和 memory cost 将会变为:

由此可见,在新建一个list时,如果能事先指定list的长度,那么代码的运行时间和所消耗的空间都将会有所改善。

代码实现(大神版):

 1 class Solution(object):
 2     def productExceptSelf(self, nums):
 3         """
 4         :type nums: List[int]
 5         :rtype: List[int]
 6         """
 7         ans = [1] * len(nums) # 首先定义一个长度为len(nums)且全为1的list,为第一个for循环中的左累乘做准备
 8         for i in range(1, len(nums)): # 左累乘
 9             ans[i] = ans[i - 1] * nums[i - 1] # 每次运算后,ans的第i个位置保存的都是nums中第i个位置之前的所有因子的积
10         right = 1
11         for i in range(len(nums) - 1, -1, -1): # 逆序的目的是为了计算右累乘
12             ans[i] *= right # 左右累乘之积即为最终的结果
13             right *= nums[i] # 右累乘
14         return ans

大神版代码分析:

核心思想也是利用左累乘器和右累乘器。

一些示意图如下:

大神版代码运行结果:

原文地址:https://www.cnblogs.com/tbgatgb/p/10921771.html