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

学习心得 做棵大树 4年前 (2020-06-04) 1512次浏览 0个评论
文章目录[隐藏]

题目

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

示例:

输入: [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]。

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 来存储左右乘积并循环迭代更新。

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

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

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

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。故只有常数级别的空间复杂度。


做棵大树 , 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明[LeetCode]238. 除自身以外数组的乘积
喜欢 (0)
[欢迎投币]
分享 (0)
关于作者:
一个整天无所事事的,有时候忽然热血的孩子
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址