剑指offer-51、构建乘积数组

题⽬描述

给定⼀个数组A[0,1,...,n-1] ,请构建⼀个数组B[0,1,...,n-1] ,其中B 中的元素B[i]=A[0]*A[1]*...*A[i-1]*A[i+1]*...*A[n-1] 。不能使⽤除法。(注意:规定B[0] =A[1] * A[2] * ... * A[n-1],B[n-1] = A[0] * A[1] * ... * A[n-2]

对于A ⻓度为1 的情况,B⽆意义,故⽽⽆法构建,因此该情况不会存在。

输⼊:[1,2,3,4,5] 输出:[120,60,40,30,24]

思路及解答

暴力

对每个B[i]都计算A中除A[i]外所有元素的乘积,双重循环,外层遍历B的每个位置,内层遍历A数组跳过当前元素

java 复制代码
public class Solution {
    public int[] multiply(int[] A) {
        if (A == null || A.length <= 1) {
            return new int[0]; // 根据题目要求,长度<=1时返回空数组
        }
        
        int n = A.length;
        int[] B = new int[n];
        
        // 遍历每个B[i]
        for (int i = 0; i < n; i++) {
            int product = 1; // 初始化乘积为1
            
            // 计算A中除A[i]外所有元素的乘积
            for (int j = 0; j < n; j++) {
                if (j != i) { // 跳过当前元素A[i]
                    product *= A[j];
                }
            }
            
            B[i] = product; // 将结果存入B[i]
        }
        
        return B;
    }
}
  • 时间复杂度:O(n²),需要嵌套循环遍历数组
  • 空间复杂度:O(1),除结果数组外只使用常数空间

左右乘积数组法(空间换时间)

使用左右两个辅助数组存储乘积信息

思路:left[i]表示A[0]到A[i-1]的乘积,right[i]表示A[i+1]到A[n-1]的乘积

java 复制代码
public class Solution {
    public int[] multiply(int[] A) {
        if (A == null || A.length <= 1) {
            return new int[0];
        }
        
        int n = A.length;
        int[] B = new int[n];
        int[] left = new int[n];  // 存储左侧乘积
        int[] right = new int[n]; // 存储右侧乘积
        
        // 初始化边界值
        left[0] = 1;     // B[0]没有左侧元素,乘积为1
        right[n-1] = 1;  // B[n-1]没有右侧元素,乘积为1
        
        // 计算左侧乘积:left[i] = A[0] × A[1] × ... × A[i-1]
        for (int i = 1; i < n; i++) {
            left[i] = left[i-1] * A[i-1];
        }
        
        // 计算右侧乘积:right[i] = A[i+1] × A[i+2] × ... × A[n-1]
        for (int i = n-2; i >= 0; i--) {
            right[i] = right[i+1] * A[i+1];
        }
        
        // 合并左右乘积得到最终结果:B[i] = left[i] × right[i]
        for (int i = 0; i < n; i++) {
            B[i] = left[i] * right[i];
        }
        
        return B;
    }
}
  • 时间复杂度:O(n),三次单层循环
  • 空间复杂度:O(n),需要两个辅助数组

矩阵视角理解: 如果把问题看作矩阵,B[i]就是去掉对角线元素A[i]后,该行所有元素的乘积。

text 复制代码
A = [1, 2, 3, 4, 5]

B[0] = 2 × 3 × 4 × 5 = 120  (去掉A[0])
B[1] = 1 × 3 × 4 × 5 = 60   (去掉A[1])  
B[2] = 1 × 2 × 4 × 5 = 40   (去掉A[2])

左右分解策略:

  • left[i]= A[0] × A[1] × ... × A[i-1] (i左边的乘积)
  • right[i]= A[i+1] × A[i+2] × ... × A[n-1] (i右边的乘积)
  • B[i] = left[i] × right[i](左右乘积相乘正好去掉A[i])

空间优化(推荐)

在方法二的基础上优化空间使用,在结果数组B上直接进行左右乘积计算。

先用B数组存储左侧乘积,再用变量动态计算右侧乘积

java 复制代码
public class Solution {
    public int[] multiply(int[] A) {
        if (A == null || A.length <= 1) {
            return new int[0];
        }
        
        int n = A.length;
        int[] B = new int[n];
        
        // 第一步:计算左侧乘积并直接存入B
        B[0] = 1; // B[0]没有左侧元素
        for (int i = 1; i < n; i++) {
            // B[i] = A[0] × A[1] × ... × A[i-1]
            B[i] = B[i-1] * A[i-1];
        }
        
        // 第二步:从右向左遍历,用temp变量累积右侧乘积
        int temp = 1; // 用于累积右侧乘积
        for (int i = n-1; i >= 0; i--) {
            // B[i]当前存储的是左侧乘积,乘以右侧乘积得到最终结果
            B[i] = B[i] * temp;
            // 更新temp,为下一个位置(i-1)准备右侧乘积
            temp = temp * A[i];
        }
        
        return B;
    }
}
  • 时间复杂度:O(n),两次遍历数组
  • 空间复杂度:O(1),除结果数组外只使用常数空间

算法步骤详解

  1. 第一步:左侧乘积计算
less 复制代码
初始: B[0] = 1
i=1: B[1] = B[0] × A[0] = 1 × 1 = 1
i=2: B[2] = B[1] × A[1] = 1 × 2 = 2  
i=3: B[3] = B[2] × A[2] = 2 × 3 = 6
i=4: B[4] = B[3] × A[3] = 6 × 4 = 24
此时B = [1, 1, 2, 6, 24] (存储的是各位置的左侧乘积)
  1. 第二步:右侧乘积整合
less 复制代码
初始: temp = 1
i=4: B[4] = 24 × 1 = 24, temp = 1 × 5 = 5
i=3: B[3] = 6 × 5 = 30, temp = 5 × 4 = 20  
i=2: B[2] = 2 × 20 = 40, temp = 20 × 3 = 60
i=1: B[1] = 1 × 60 = 60, temp = 60 × 2 = 120
i=0: B[0] = 1 × 120 = 120, temp = 120 × 1 = 120
最终B = [120, 60, 40, 30, 24]
相关推荐
百炼成神 LV@菜哥4 分钟前
Kylin Linux V10 aarch64安装DBeaver
java·linux·服务器·kylin
有代理ip19 分钟前
成功请求的密码:HTTP 2 开头响应码深度解析
java·大数据·python·算法·php
好好沉淀40 分钟前
ES 脚本核心语法:ctx._source [‘group_id‘]
java·elasticsearch·script
李慕婉学姐1 小时前
【开题答辩过程】以《基于Spring Boot的疗养院理疗管理系统的设计与实现》为例,不知道这个选题怎么做的,不知道这个选题怎么开题答辩的可以进来看看
java·spring boot·后端
tb_first1 小时前
SSM速通2
java·javascript·后端
qq_12498707531 小时前
基于协同过滤算法的运动场馆服务平台设计与实现(源码+论文+部署+安装)
java·大数据·数据库·人工智能·spring boot·毕业设计·计算机毕业设计
大飞哥~BigFei1 小时前
自定义注解记录接口切面log日志入库优化
java
人道领域1 小时前
javaWeb从入门到进阶(maven高级进阶)
java·spring·maven
一路向北⁢1 小时前
Spring Boot 3 整合 SSE (Server-Sent Events) 企业级最佳实践(一)
java·spring boot·后端·sse·通信
风象南1 小时前
JFR:Spring Boot 应用的性能诊断利器
java·spring boot·后端