剑指offer——构建乘积数组

这是剑指offer中数组类知识点中第3道题目,此题目的难度为两颗星,相对比较简单,原题目链接:构建乘积数组

为方便直接查看,先抄一下题目。

题目描述:

给定一个数组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与数组B是同维的,且长度也相同。其中数组A已知,求数组B。

数组B中各元素是数组A中所有元素的乘积(数组A中对应位置的元素除外)

由于题目要求不能使用除法,所以我们不能使用(直接算出数组A中所有元素的乘积,然后再除以该位置的元素;最后得到数组B中的一个元素。)这种方法。

那么就只能逐个求数组B中的元素了。由于B[i]是A中除A[i]之外,其余元素的乘积,那么只要其余元素有0,则B[i]就必然是0

再进一步,如果数组A中有两个或两个以上0元素则数组B必须是全0元素。

既然考虑到了0元素,那么当A[i]=0时,数组B是什么情况呢?显然,只有B[i]不为0,其余都为0。

数组A中没有0元素时,只能逐个按照公式来求B[i]了

实现代码如下:

 1 public int[] multiply(int[] A) {
 2         if(A==null || A.length <= 0){
 3             return null;
 4         }
 5         int[] B = new int[A.length];
 6         int zero_num = 0;    //记录数组A中0元素的个数
 7         for(int i=0; i<A.length; i++){
 8             if(A[i] == 0){
 9                 zero_num++;
10                 if(zero_num>1){    //若数组A中0元素个数超过1个,则数组B必全为0元素
11                     return B;
12                 }
13                 B[i]=1;
14                 for(int j=0; j<A.length; j++){
15                     if(j!=i){
16                         B[i]*=A[j];
17                     }
18                 }
19                 return B;    //若数组A中只有一个0元素,则数组B仅此位置不为0
20             }else{    //若始终无0元素,则逐个计算B[i]
21                 B[i]=1;
22                 for(int j=0; j<A.length; j++){
23                     if(j!=i){
24                         B[i]*=A[j];
25                     }
26                 }
27             }
28         }
29         return B;
30     }
View Code

此代码在牛客网上运行时间为15ms。

 进阶方法:

虽然上述方法已经尽量避免了出现0情况下的计算;然而如果数组A中没有出现0元素时,此方法则是对数组B中的每个元素都重新进行连乘求解。此种情况下并没有进行简化

假设不考虑0元素这种情况,数组B中每个元素都需要遍历一次数组A;那么能否减少遍历次数,从而减少乘法计算次数呢?如果能找到数组B中元素之间的关系,那么就可以减少大量的乘法计算。

那么将数组B各元素的表达式列出来;如下图所示:

其中数组B中各元素等于该行的乘积;通过观察上图可以发现:可以把每一行中1作为分界线,假设1左边的各部分的乘积记为Ci,1右边的各部分的乘积记为Di

就可以得到Bi=Ci*Di;Bi+1=Ci+1*Di+1;Bi-1=Ci-1*Di-1。图中可以明显发现如下关系:Ci=Ci-1*Ai-1Di=Di+1*Ai+1。据此关系,便可以减少乘法计算次数了。实现代码如下:

 1 public int[] multiply(int[] A) {
 2         if(A==null || A.length <= 0){
 3             return null;
 4         }
 5         int[] B = new int[A.length];
 6         int left = 1;    //初始化左半部分Ci,C0为1;每次计算的结果Ci暂时保存在Bi中
 7         int right = 1;    //初始化右半部分Di,D(n-1)为1。Di与Ci的计算结果为最终的Bi
 8         B[0] = left;    //C0不用计算,先直接放在B0处
 9         for(int i=1; i<A.length; i++){
10             left = left*A[i-1];    //Ci = C(i-1)*A(i-1)
11             B[i] = left;
12         }
13         //B(n-1)=C(n-1)*D(n-1)=C(n-1);所以直接从B(n-2)开始计算
14         for(int j=A.length-2; j>=0; j--){
15             right = right * A[j+1];    //Di = D(i+1)*A(i+1)
16             B[j] = B[j] * right;        //Bi = Ci * Di
17         }
18         return B;
19     }
View Code

此代码在牛客网上的运行时间为18ms。

最终结果:

接下来再结合最开始考虑过的针对0元素的处理方法,将此处的代码替换掉0元素未出现的那部分代码即可,最终代码如下:

 1 public int[] multiply(int[] A) {
 2         if(A==null || A.length <= 0){
 3             return null;
 4         }
 5         int[] B = new int[A.length];
 6         
 7         //考虑数组A中有0元素的情况
 8         int zero_num = 0;    //记录数组A中0元素的个数
 9         for(int i=0; i<A.length; i++){
10             if(A[i] == 0){
11                 zero_num++;
12                 if(zero_num>1){    //若数组A中0元素个数超过1个,则数组B必全为0元素
13                     return B;
14                 }
15                 B[i]=1;
16                 for(int j=0; j<A.length; j++){
17                     if(j!=i){
18                         B[i]*=A[j];
19                     }
20                 }
21                 return B;    //若数组A中只有一个0元素,则数组B仅此位置不为0
22             }
23         }
24         
25         //若数组A中没有0元素的情况
26         int left = 1;    //初始化左半部分Ci,C0为1;每次计算的结果Ci暂时保存在Bi中
27         int right = 1;    //初始化右半部分Di,D(n-1)为1。Di与Ci的计算结果为最终的Bi
28         B[0] = left;    //C0不用计算,先直接放在B0处
29         for(int i=1; i<A.length; i++){
30             left = left*A[i-1];    //Ci = C(i-1)*A(i-1)
31             B[i] = left;
32         }
33         //B(n-1)=C(n-1)*D(n-1)=C(n-1);所以直接从B(n-2)开始计算
34         for(int j=A.length-2; j>=0; j--){
35             right = right * A[j+1];    //Di = D(i+1)*A(i+1)
36             B[j] = B[j] * right;        //Bi = Ci * Di
37         }
38         return B;
39     }
View Code

此代码在牛客网上的运行时间为14ms。

原文地址:https://www.cnblogs.com/shengguilv/p/12858972.html