剑指Offer:[第23天 数学(简单)]--->构建乘积数组


一、题目描述

给定一个数组 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


二、思路分析

注:思路分析中的一些内容和图片参考自力扣各位前辈的题解,感谢他们的无私奉献

思路

如果可以使用除法则非常简单,直接将 A A A 数组中的各个元素乘起来,然后去除以 A [ i ] A[i] A[i] 就是 B [ i ] B[i] B[i] 的值。但是本题的难点在于不能使用除法,即需要只用乘法生成数组 B B B。根据题目对 B [ i ] B[i] B[i] 的定义,可列表格,如下图所示
在这里插入图片描述
根据表格的主对角线(全为1),可将表格分为上三角和下三角两部分。分别迭代计算下三角和上三角两部分的乘积,即可不使用除法就获得结果。
常规算法流程:
①初始化:数组 l e f t left left、数组 r i g h t right right、数组 r e s res res。其中 l e f t [ 0 ] = 1 left[0] = 1 left[0]=1 r i g h t [ l e n − 1 ] = 1 right[len-1]=1 right[len1]=1
②计算 B [ i ] B[i] B[i] 的下三角各元素的乘积, l e f t [ i ] = B [ i − 1 ] ∗ l e f t [ i − 1 ] left[i]=B[i-1]*left[i-1] left[i]=B[i1]left[i1]
③计算 B [ i ] B[i] B[i] 的上三角各元素的乘积, r i g h t [ i ] = B [ i + 1 ] ∗ r i g h t [ i + 1 ] right[i]=B[i+1]*right[i+1] right[i]=B[i+1]right[i+1]
④将 l e f t [ i ] × r i g h t [ i ] left[i] \times right[i] left[i]×right[i] 保存在 r e s [ i ] res[i] res[i]
⑤返回 r e s res res
改进(优化空间复杂度至O(1)):
①初始化:数组 B B B,其中 B [ 0 ] = 1 B[0] = 1 B[0]=1,辅助变量 t m p = 1 tmp = 1 tmp=1
②计算 B [ i ] B[i] B[i] 的下三角各元素的乘积,直接乘入 B [ i ] B[i] B[i]
③计算 B [ i ] B[i] B[i] 的上三角各元素的乘积,记为 t m p tmp tmp,并乘入 B [ i ] B[i] B[i]
④返回 B B B
案例分析:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
复杂度分析:
时间复杂度 O ( N ) \rm{O(N)} O(N):其中N为数组长度,两轮遍历数组a,使用O(N)时间
空间复杂度 O ( 1 ) \rm{O(1)} O(1):变量tmp使用常数大小额外空间(数组b作为返回值,不计入复杂度考虑)


三、整体代码

常规算法整体代码如下

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int* constructArr(int* a, int aSize, int* returnSize){
    *returnSize = aSize;
    if(aSize == 0){
        return NULL;
    }
    int* left = (int*)malloc(sizeof(int)*aSize);
    int* right = (int*)malloc(sizeof(int)*aSize);
    int* res = (int*)malloc(sizeof(int)*aSize);

    left[0] = 1;
    right[aSize-1] = 1;

    for(int i = 1; i < aSize; i++){
        left[i] = left[i-1] * a[i-1]; 
    }
    for(int i = aSize - 2; i >= 0; i--){
        right[i] = right[i+1] * a[i+1];
    }
    for(int i = 0; i < aSize; i++){
        res[i] = left[i] * right[i];
    }

    return res;
}

运行,测试通过
在这里插入图片描述


优化后的算法整体代码如下

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int* constructArr(int* a, int aSize, int* returnSize){
    *returnSize = aSize;
    if(aSize == 0){
        return NULL;
    }
    int* b = (int*)malloc(sizeof(int)*aSize);
    b[0] = 1;
    int tmp = 1;

    for(int i = 1; i < aSize; i++){
        b[i] = b[i-1] * a[i-1];
    }
    for(int i = aSize-2; i >=0; i--){
        tmp *= a[i+1];
        b[i] *= tmp;
    }

    return b;
}

运行,测试通过
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

知初与修一

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值