zoukankan      html  css  js  c++  java
  • 剑指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。

  • 相关阅读:
    仿EXT风格,仿EXT功能,Jquery页签插件,EM.Tabs 1.1,新增事件处理机制,可拓展
    插入数据的同时,返回ID值
    xml格式的string转换为dataset
    ASP.NET缓存:方法分析和实践示例
    showModalDialog()方法使用详解 (转)
    160809325贺彦
    vncdotool A command line VNC client
    使Windows服务以控制台方式调试
    DIV CSS阴影
    允许与桌面交互[.net Windows服务]
  • 原文地址:https://www.cnblogs.com/shengguilv/p/12858972.html
Copyright © 2011-2022 走看看