1.题目描述
给定一个数组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];)
2.具体算法
class Solution {
public:
vector<int> multiply(const vector<int>& A) {
int n = A.size();//定义一个n,用来记录数组A的大小
vector<int> B(n,1);
vector<int> B1(n,1);
vector<int> B2(n,1);
for(int j=1;j<n;j++)
{
B1[j] = B1[j-1]*A[j-1];//找出B1[j]与B1[j-1]之间的递推关系
}
for(int k=n-2;k>=0;k--)
{
B2[k] = B2[k+1]*A[k+1];//找出B2[k]与B2[k-1]之间的递推关系
}
for(int p=0;p<n;p++)
{
B[p]=B1[p]*B2[p];//用数组B1和B2来表示B
}
return B;//返回数组B
}
};
3.算法推导
可能只看上面的算法会觉得很抽象,那么下面就以举例的形式来将这个抽象的问题具体化,把上面算法所要用到的所有的公式都推导出来:
数组B1用来记录A[0]~A[i-1]的数据之间的乘积,而数组B2用来记录A[i+1] ~A[n-1]的数据之间的乘积。(i为0和n时除外,B[0] = A[1] * A[2] * … * A[n-1],B[n-1] = A[0] * A[1] * … * A[n-2])
假设n为5,A[0]=1,A[1]=2,A[2]=3,A[3]=4,A[4]=5;
B[0]=1 * A[1] * A[2] * A[3] * A[4] =B1[0] * B2[0]=2 * 3 * 4 * 5
B[1]=A[0] * 1 * A[2]* A[3] * A[4] =B1[1] * B2[1]=1 * 3 * 4 * 5
B[2]=A[0] * A[1] * 1 * A[3]* A[4] =B1[2] * B2[2]=1 * 2 * 4 * 5
B[3]=A[0] * A[1] * A[2] * 1* A[4] =B1[3] * B2[3]=1 * 2 * 3 * 5
B[4]=A[0] * A[1] * A[2] * A[3]* 1=B1[4] * B2[4]=1 * 2 * 3 * 4
由上面的式子不难发现:
**B1[j] = B1[j-1]A[j-1]
B2[k] = B2[k+1]A[k+1]
4.算法总结
其实我开始写代码的时候,提交的代码是合格的,但是并不是以上的那个形式,而是如下所示:
class Solution {
public:
vector<int> multiply(const vector<int>& A) {
int n = A.size();
vector<int> B(n,1);
vector<int> B1(n,1);
vector<int> B2(n,1);
B1[1]=A[0];
for(int j=2;j<n;j++)
{
B1[j] = B1[j-1]*A[j-1];
}
B2[n-2] = A[n-1];
for(int k=n-3;k>=0;k--)
{
B2[k] = B2[k+1]*A[k+1];
}
for(int p=0;p<n;p++)
{
B[p]=B1[p]*B2[p];
}
return B;
}
};
差别主要就是如下两行:
B1[1]=A[0];
for(int j=2;j<n;j++)
{
B1[j] = B1[j-1]*A[j-1];
}
B2[n-2] = A[n-1];
for(int k=n-3;k>=0;k--)
{
B2[k] = B2[k+1]*A[k+1];
}
按照数学推导的思路,我刚开始是确定了数组B1和B2的初始推导元素B1[1]和B2[n-2]的值,然后再进行剩下的元素值的推导,但后来反应过来B1[0]和B2[n-1]的值都是1,所以没必要这样单独定义B1[1]和B2[n-2]的值,直接写成如下形式即可:
for(int j=1;j<n;j++)
{
B1[j] = B1[j-1]*A[j-1];//找出B1[j]与B1[j-1]之间的递推关系
}
for(int k=n-2;k>=0;k--)
{
B2[k] = B2[k+1]*A[k+1];//找出B2[k]与B2[k-1]之间的递推关系
}
这两种写法虽然在复杂度和方法思路上都没有太大的区别,但是修改之后的代码就是显得整齐和规整,而且思路和逻辑上更加完善,有时候一个算法关系到逻辑推导的时候,确实应该多检查检查。