题目:给定一个数组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]。不能使用除法。算法
解析:这道题,直观的解法是:设置一个循环(由0到n-1),计算B[i]时,忽略掉A[i]项,把数组A中的其余项所有相乘,即获得B[i]=A[0]*A[1]*...*A[i-1]*A[i+1]*...*A[n-1]。这样,一次循环事后,就能够在没有除法的条件下,获得数组B中全部的值。那么,这种解法的时间复杂度和空间复杂度是多少呢?时间复杂度:因为循环由0~n-1,即o(n),在每次循环中,要执行n-1次乘法运算,因此这种解法的时间复杂度为o(n)*o(n-1)=o(n2);空间复杂度为o(1)。数组
显然,上面这种解法的时间复杂度较高,那么有没有o(n)的解法呢?咱们不妨分析一下上面这种解法的问题,从而找到能够优化的突破口。上面的复杂度由两部分组成,第一部分:循环由0~n-1,显然咱们须要计算每个B[i],无论怎样,咱们都没办法去掉这样的基本循环,也就是说这一部分带来了时间复杂度o(n)不能再进行优化;第二部分:每一次计算B[i]都须要进行n-1次乘法,也便是须要计算n次n-1个数相乘,细心的人能够发现,这里面有不少的乘法是重复的,正是因为这部分重复的乘法计算形成咱们的时间复杂度很高。那么,有没有办法只计算一次这样的n-1个数相乘呢?咱们能够定义两个中间数组来存储已经计算过得乘法结果,这样,在进行下一个B[i]计算时,咱们只须要完成一次乘法就能够获得B[i]的结果了。这样,时间复杂度就变成了o(1),整个算法时间复杂度就降成了o(n)。具体分析思路以下:优化
首先,咱们能够将数组B表示成矩阵的形式以下:spa
B[0] | 1 | A[1] | A[2] | ... | A[n-2] | A[n-1] |
B[1] | A[0] | 1 | A[2] | ... | A[n-2] | A[n-1] |
B[2] | A[0] | A[1] | 1 | ... | A[n-2] | A[n-1] |
... | ... | ... | ... | 1 | ... | ... |
B[n-2] | A[0] | A[1] | A[2] | ... | 1 | A[n-1] |
B[n-1] | A[0] | A[1] | A[2] | ... | A[n-2] | 1 |
如上图所示,矩阵的每一行表明数组B的一个元素,从上往下,依次是B[0],B[1],...B[n-2],B[n-1]。那么,咱们能够将每个B[i]当作两部分,分别用C[i]和D[i]表示。其中C[i] = A[0]*A[1]*...*A[i-1],D[i] = A[i+1]*...*A[n-2]*A[n-1],这样,B[i]=C[i]*1*D[i]。也就是说,咱们能够每次更新数组C[i]和D[i],即C[i]=C[i-1]*A[i-1]和D[i]=D[i+1]*A[i+1](须要说明的是,这里C[i]的更新是按照从前日后,而D[i]则是从后往前),从而为咱们节省了大量的重复的乘法计算,使得时间复杂度降为o(n)。code
int[] multiply(int[] A){ if(A==null||A.length<=0)//边界条件,最好附带上 return null; int n = A.length; int[] B = new int[n]; B[0]=1; /*更新C[i],这里咱们不另外定义数组,直接将C[i]的计算结果存储在B[i]中, 这样,再将D[i]的结果直接乘以B[i](此时B[i]等于C[i]),就获得了最终 的B[i],显然为咱们又节省了很多空间存储。 */ for(int i=1;i<n;i++){ B[i]=B[i-1]*A[i-1]; } int temp =1; //更新D[i],这里要从n-2开始,由于B[n-1]已获得最终结果 for(int j=n-2;j>=0;j--){ temp*=A[j+1]; B[j]*=temp; }
return B; }
显然,上述解法的时间复杂度为:2*o(n)*2=o(n)。blog