构建乘积数组(剑指offer 66)Java双向遍历

简介: 给定一个数组 A[0,1,…,n-1],请构建一个数组 B[0,1,…,n-1],其中 B[i] 的值是数组 A 中除了下标 i 以外的元素的积, 即 B[i]=A[0]×A[1]×…×A[i-1]×A[i+1]×…×A[n-1]。不能使用除法。

一、题目描述



给定一个数组 A[0,1,…,n-1],请构建一个数组 B[0,1,…,n-1],其中 B[i] 的值是数组 A 中除了下标 i 以外的元素的积, 即 B[i]=A[0]×A[1]×…×A[i-1]×A[i+1]×…×A[n-1]。不能使用除法。


示例:

输入: [1,2,3,4,5]

输出: [120,60,40,30,24]

 

提示:

所有元素乘积之和不会溢出 32 位整数

a.length <= 100000


二、思路讲解


       

本题难点在于不能使用除法。

       

首先我想到的是用双循环,从头乘到尾,碰到i就跳过。结果是超时。


那我就不在循环里判断,先算0~i-1的乘积,再算i+1~length-1的乘积。还是超时。

       

那我只好申请两个数组,dp1和dp2,dp1用来保存前i项的积,dp2用来保存从第i项到最后的积。


三、代码实现



class Solution {
    public int[] constructArr(int[] a) {
        //如果是空数组,则返回空数组
        if(a.length == 0){
            return new int[0];
        }
        int []b = new int[a.length];
        int result= 1;
        int []dp1 = new int[a.length];  //第i项表示数组a中前i项的积
        int []dp2 = new int[a.length];  //第i项表示数组a中从第i项到最后的积
        for(int i=0; i<a.length; i++){
            result = result * a[i];
            dp1[i] = result;         
        }
        result=1;
        for(int i=a.length-1; i>=0; i--){
            result = result * a[i];
            dp2[i] = result;
        }
        b[0] = dp2[1];
        for(int i=1; i<a.length-1; i++){
            b[i] = dp1[i-1] * dp2[i+1];
        }
        b[a.length-1] = dp1[a.length-2];
        return b;
    }
}


四、代码优化



这个代码中额外使用了2n长度的数组,其实是不需要的。只需要用一个变量保存,直接赋值到b[i]上就可以了。

class Solution {
    public int[] constructArr(int[] a) {
        //如果是空数组,则返回空数组
        if(a.length == 0){
            return new int[0];
        }
        int []b = new int[a.length];
        //先把b[i]赋值为前i-1项的积
        b[0] = 1;
        for(int i=1; i<a.length; i++){
            b[i] = b[i-1] * a[i-1];
        }
        int temp = a[a.length-1];
        //再把第i+1项到最后的积乘到b[i]上
        for(int i=a.length-2; i>=0; i--){
            b[i] = b[i] * temp;
            temp = temp * a[i];
        }
        return b;
    }
}


时间复杂度:        O(n)        遍历了两遍数组a

     

空间复杂度:        O(1)

相关文章
|
15天前
|
人工智能 前端开发 Java
基于开源框架Spring AI Alibaba快速构建Java应用
本文旨在帮助开发者快速掌握并应用 Spring AI Alibaba,提升基于 Java 的大模型应用开发效率和安全性。
基于开源框架Spring AI Alibaba快速构建Java应用
|
11天前
|
存储 Java 开发者
在 Java 中,如何遍历一个 Set 集合?
【10月更文挑战第30天】开发者可以根据具体的需求和代码风格选择合适的遍历方式。增强for循环简洁直观,适用于大多数简单的遍历场景;迭代器则更加灵活,可在遍历过程中进行更多复杂的操作;而Lambda表达式和`forEach`方法则提供了一种更简洁的函数式编程风格的遍历方式。
|
16天前
|
Java 数据库连接 数据库
如何构建高效稳定的Java数据库连接池,涵盖连接池配置、并发控制和异常处理等方面
本文介绍了如何构建高效稳定的Java数据库连接池,涵盖连接池配置、并发控制和异常处理等方面。通过合理配置初始连接数、最大连接数和空闲连接超时时间,确保系统性能和稳定性。文章还探讨了同步阻塞、异步回调和信号量等并发控制策略,并提供了异常处理的最佳实践。最后,给出了一个简单的连接池示例代码,并推荐使用成熟的连接池框架(如HikariCP、C3P0)以简化开发。
35 2
|
21天前
|
Java 程序员 编译器
Java|如何正确地在遍历 List 时删除元素
从源码分析如何正确地在遍历 List 时删除元素。为什么有的写法会导致异常,而另一些不会。
18 3
|
23天前
|
存储 缓存 算法
Java 数组
【10月更文挑战第19天】Java 数组是一种非常实用的数据结构,它为我们提供了一种简单而有效的方式来存储和管理数据。通过合理地使用数组,我们能够提高程序的运行效率和代码的可读性。更加深入地了解和掌握 Java 数组的特性和应用,为我们的编程之旅增添更多的精彩。
31 4
|
23天前
|
存储 缓存 算法
提高 Java 数组性能的方法
【10月更文挑战第19天】深入探讨了提高 Java 数组性能的多种方法。通过合理运用这些策略,我们可以在处理数组时获得更好的性能表现,提升程序的运行效率。
19 2
|
1月前
|
存储 Java
Java“(array) <X> Not Initialized” (数组未初始化)错误解决
在Java中,遇到“(array) &lt;X&gt; Not Initialized”(数组未初始化)错误时,表示数组变量已被声明但尚未初始化。解决方法是在使用数组之前,通过指定数组的大小和类型来初始化数组,例如:`int[] arr = new int[5];` 或 `String[] strArr = new String[10];`。
|
缓存 安全 网络协议
|
8天前
|
安全 Java 测试技术
Java并行流陷阱:为什么指定线程池可能是个坏主意
本文探讨了Java并行流的使用陷阱,尤其是指定线程池的问题。文章分析了并行流的设计思想,指出了指定线程池的弊端,并提供了使用CompletableFuture等替代方案。同时,介绍了Parallel Collector库在处理阻塞任务时的优势和特点。