前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >[LeetCode]238. 除自身以外数组的乘积

[LeetCode]238. 除自身以外数组的乘积

作者头像
做棵大树
发布2022-09-27 19:53:22
发布2022-09-27 19:53:22
35000
代码可运行
举报
文章被收录于专栏:代码日志代码日志
运行总次数:0
代码可运行

题目

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

示例:

代码语言:javascript
代码运行次数:0
运行
复制
输入: [1,2,3,4]
输出: [24,12,8,6]

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

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

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

解法

拿到题目首先的想法是:两次for循环,一次乘积一次做除法。但是后来发现说明中注明不要使用除法,便只能向其他方法。

既然是算除了自己之外的累乘,便可以以当前所在位置为分割点,分别计算左侧元素乘积右侧元素乘积,之后再进行相乘。

对此由以下解法:

算法一(摘自LeetCode官方解法):

初始化两个空数组 L 和 R。对于给定索引 i,L[i] 代表的是 i 左侧所有数字的乘积,R[i] 代表的是 i 右侧所有数字的乘积。 我们需要用两个循环来填充 L 和 R 数组的值。对于数组 L,L[0] 应该是 1,因为第一个元素的左边没有元素。对于其他元素:L[i] = L[i-1] * nums[i-1]。 同理,对于数组 R,R[length-1] 应为 1。length 指的是输入数组的大小。其他元素:R[i] = R[i+1] * nums[i+1]。 当 R 和 L 数组填充完成,我们只需要在输入数组上迭代,且索引 i 处的值为:L[i] * R[i]。

代码语言:javascript
代码运行次数:0
运行
复制
class Solution {
    public int[] productExceptSelf(int[] nums) {
  int arrLen = nums.length;
  int[] leftNums = new int[arrLen];
  int[] rightNums = new int[arrLen];
  leftNums[0] = 1;rightNums[arrLen-1] = 1;
  
  for(int i = 1; i < arrLen; i++){
   leftNums[i] = leftNums[i-1] * nums[i-1];
   rightNums[arrLen-i-1] = rightNums[arrLen-i] * nums[arrLen-i];
  }
  
  for(int i = 0; i < arrLen; i++){
   leftNums[i] *= rightNums[i];
  }
  
  return leftNums;
 }
}

复杂度分析

时间复杂度:O(N),其中 N 指的是数组 nums 的大小。预处理 L 和 R 数组以及最后的遍历计算都是 O(N) 的时间复杂度。

空间复杂度:O(N),其中 N 指的是数组 nums 的大小。使用了 L 和 R 数组去构造答案,L 和 R 数组的长度为数组 nums 的大小。

算法二:共享数组方式

整体思路和官方解题思路相同:左乘*右乘。

定义返回数组 returnNums 并将其看作共享数组,同时从左右两端填充数据;之后定义 left,right 来存储左右乘积并循环迭代更新。

在两指针交会前,只需对数组进行简单的填充即可;

在两者交互时(仅发生在奇数长度)其填充值为 left*right

两者交汇后,数组的值应填入最终值:因为左侧部分已经存储了左乘积,而即将计算得到右乘积;右侧部分已存储了右乘积,即将获得左乘积。故直接相乘即可。**returnNums[i] = left 和 returnNums[j] = right

代码语言:javascript
代码运行次数:0
运行
复制
class Solution {
    public int[] productExceptSelf(int[] nums) {
        int arrLen = nums.length;

        int[] returnNums = new int[arrLen];
        int left = 1, right = 1;    // 临时存储
        returnNums[0] = 1; returnNums[arrLen-1] = 1;
        for(int i = 1, j = arrLen-2; i < arrLen; i++, j--){
            left *= nums[i-1];
            right *= nums[j+1];
            if(i < j){  // 两指针为交会
                returnNums[i] = left;
                returnNums[j] = right;
            }else if(i == j){   // 两指针重合,奇数位情况下发生
                returnNums[i] = left*right;
            }else{              // 两指针错位
                returnNums[i] *= left;
                returnNums[j] *= right;
            }
        }

        return returnNums;
    }
}

复杂度分析

时间复杂度:O(N),同上一解题方式相同,进行了一次长度为N的循环,其时间复杂度为O(N)。

空间复杂度:O(1),题目中所述,返回数组的空间不算,故所使用的额外存储空间为 leftright。故只有常数级别的空间复杂度。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-06-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 做棵大树 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 题目
    • 解法
      • 算法一(摘自LeetCode官方解法):
      • 算法二:共享数组方式
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档