1、解题思路
这道题想了半天一直没想出什么好的办法,后来在纸上写写画画,终于找到了一种实现方法。
首先,想的是列个如下的数组出来:
2 4 5 3
4 2 2 2
5 5 4 4
3 3 3 5
第一行每个元素对应的其余元素的乘积 为同列其他元素的乘积,但这样一来求解乘积的复杂度仍是o(n*n)
然后,开始用函数去抽象每个元素的结果,结果用T表示,乘积用S表示,发现T0 = S(1..N-1),,T1=S(0) * S(2..N-1),
方法瞬间就清晰了:只要求出 数组中位于 指定元素K前所有元素的乘积S1(0..K-1) 与 位于该元素后所有元素的乘积S2(K+1...N-1),S1与S2相乘便得到了元素K的题解值TK。那么通过顺序、倒序两次遍历数组,计算乘积,就能得到每个元素前后元素的乘积值,也就得到了要求解的值。
改进:使用常数空间求解。
仍是通过计算某元素左右两侧元素乘积来得到求解值,可以发现,求解值T在顺序遍历时即等于左侧元素乘积S1,倒序遍历时等于S1与S2乘积,因为两次遍历是依次进行的,所以可以直接将S1保存到T,再更新T值为S1*S2即可。
顺序遍历时,TK 可用T(K-1)*A(K-1)表示,A(K)为元素K的值,故不需要中间变量;
倒序遍历时,TK 可用T(K) * S(K+1...N-1)表示,需要新增局部变量保存右侧元素乘积的值。
2、有效题解
int* productExceptSelf(int* nums, int numsSize, int* returnSize) {
if (NULL == nums || NULL == returnSize)
{
return NULL;
}
int *ret = (int *)malloc(sizeof(int) * numsSize);
int *productBefore = (int *)malloc(sizeof(int) *numsSize);
int *productAfter = (int *)malloc(sizeof(int) *numsSize);
int i, j;
productBefore[0] = 1;
productAfter[numsSize - 1] = 1;
for (i = 1; i < numsSize; i++)
{
productBefore[i] = productBefore[i-1] * nums[i-1];
}
for (j = numsSize - 2; j >= 0; j--)
{
productAfter[j] = productAfter[j+1] * nums[j+1];
}
for (i = 0; i <numsSize; i++)
{
ret[i] = productBefore[i] * productAfter[i];
}
free(productBefore);
free(productAfter);
*returnSize = numsSize;
return ret;
}
空间优化解
int* productExceptSelf(int* nums, int numsSize, int* returnSize) {
if (NULL == nums || NULL == returnSize)
{
return NULL;
}
int *ret = (int *)malloc(sizeof(int) * numsSize);
int i, j;
int right = 1;
ret[0] = 1;
for (i = 1; i < numsSize; i++)
{
ret[i] = ret[i-1] * nums[i-1];
}
ret[numsSize-1] *= right;
for (j = numsSize - 2; j >= 0; j--)
{
ret[j] *= right * nums[j+1];
right = right * nums[j+1];
}
*returnSize = numsSize;
return ret;
}
3、复杂度分析
遍历两次数组,时间复杂度o(n),新增两个数组保存中间值,空间复杂度o(n)。
4、小结
1、通过举例去发现规律,同时结合正向、逆向,一维、多维角度进行摸索。
2、思考使用常数空间求解。