1. 前言

今天是力扣刷题的第三天啊,必须上点难度,不然太简单都容易刷腻了。题型依旧是数组类型,经过这几道数组类型的洗礼渐渐摸出点门道来了,给大家分享一下心得体会,下面就进入正题。

2. 题目描述

给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积。题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。请 不要使用除法,且在 O(n) 时间复杂度内完成此题。

示例 1:

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

示例 2:

输入: nums = [-1,1,0,-3,3]
输出: [0,0,9,0,0]

提示:

  • 2 <= nums.length <= 105
  • -30 <= nums[i] <= 30
  • 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内

3. 题目分析

简单说一下题目的意思,就是给你一个数组 nums,你返回一个数组 answer,answer 数组中的元素对应 nums 中的元素,answer 的值等于 nums 中所有元素乘积的总和除去当前数。当然,题目规定不能用出发,也不能用出发,因为元素中有 0。

那么此时我们就会想,怎么不用除法把这个结果拿到呢?既然不能除,只能老老实实地去一个一个乘了。但是如果每次都把其他地数乘一次,那么时间复杂度就是 O(n^2) 了,显然不符合要求,那么怎么办呢?

其实绝大部分对于时间复杂度地优化都是基于去除重复步骤这个思路,这道题就是如何去除重复地步骤,你想啊,如果一共有 【1,2,3,4,5】5个数,那么计算 1 的时候,answer[0]=2345。计算 2 的时候 answer[1] = 1345,以此类推其中 45 重复计算了 3次,345 重复计算了 2 次。如果我们每次不去计算,45 就是 20 可以吗?

当然可以,我们用另一个数组把它们的结果记录下来 ,定义一个数组 right 记录从右往左的乘积结果,right[i] = nums[i]nums[i+1]...*nums[nums.length-1]。有了这个数组,我们就能直接拿到结果了,比如算 answer[0]=right[1],answer[1]=right[2]*1...等等,这样我们就优化了右边的乘积。

同理我们按照上面的思路,把从作往右的数组记为 right,这样,我们就可以说 answer[i] =left[i-1]*right[i+1],

下面就是实现的代码:

class Solution {
    public int[] productExceptSelf(int[] nums) {
            //最后一位是前面所有的数的乘积
            //倒数第二位是前面所有的乘积*最后一位
            //倒数第三位是前面所有的乘积*最后一位*倒数第二位
            //第一位是后面所有的乘积
            //当前位置的乘积,等于左右两边乘积相乘,所以只需要记录两次就行了,天才!
        int len = nums.length;
        if(len<=1){
            return nums;
        }
        int[] left = new int[len];
        int[] right = new int[len];
        left[0]=nums[0];
        right[len-1]=nums[len-1];
        for(int i=1;i<len;i++){
            left[i]=left[i-1]*nums[i];
        }
        for(int i=len-2;i>=0;i--){
            right[i]=right[i+1]*nums[i];
        }
        
        int[] answer = new int[len];
        answer[0] = right[1];
        answer[len-1]=left[len-2];
        for(int i=1;i<len-1;i++){
            answer[i]=left[i-1]*right[i+1];
        }
        return answer;
    }
}

注释里面是我写题的时候思考的过程,哈哈哈。一点一点分析,然后有了一点灵感,就迎刃而解了。这里代码没有润色过,比较粗糙,思路和上面说的一样就是在边界值的时候做了处理,比如最左边和最右边其实就直接等于 right[1] 和 left[len-1] 。为了防止数组越界,把长度小于 2 的直接返回。然后就是正常处理了,时间复杂度是 O(3n),但是由于3是常数,所以不会计入时间复杂度中依旧是 O(n),满足条件。

对于数组这类算法题,最常用到的思想就是累计,不管是乘还是加,把它们遍历的结果记录下来,几乎一大半都是这种题,或者类似的变体,比如这道题,分别从左往右,从右往左遍历了两次,记录了了两次结果。但是基本思想是相同的,所以遇到这种题,大家可以多往这方面去匹配,类似于中学时期匹配完全平方公式一样,哈哈哈。

4. 最后结果

屏幕截图 2024-03-13 211310.png