leetcode238. 除自身以外数组的乘积(偶遇无符号数的陷阱)

这道题看完官方的题解后还是很容易理解的:

题解:初始化两个空数组 L 和 R。对于给定索引 i,L[i] 代表的是 i 左侧所有数字的乘积,R[i] 代表的是 i 右侧所有数字的乘积
我们需要用两个循环来填充 L 和 R 数组的值。对于数组 L,L[0] 应该是 1,因为第一个元素的左边没有元素。对于其他元素:L[i] = L[i-1] * nums[i-1]。
同理,对于数组 R,R[length-1] 应为 1。length 指的是输入数组的大小。其他元素:R[i] = R[i+1] * nums[i+1]。
当 R 和 L 数组填充完成,我们只需要在输入数组上迭代,且索引 i 处的值为:L[i] * R[i]。

class Solution {
public:
    vector<int> productExceptSelf(vector<int>& nums) {
        vector<int> L(nums.size()),R(nums.size()),answer(nums.size());
       //初始化为1,方便后续的运算
        L[0] = 1,R[nums.size()-1] = 1;
        //这里有点动态规划的感觉,后一个状态依赖于前几个状态。
        //初始化左数组
        for(uint64_t i = 1;i<nums.size();i++){
            L[i] = L[i-1] * nums[i-1];
        }
        //初始化右数组
        for(uint64_t j = nums.size() - 2;j >= 0 ; j--){
            R[j] = R[j+1] * nums[j+1];
        }
        //进行计算
        for(uint64_t k = 0;k < nums.size();k++){
            answer[k] = L[k]*R[k]; //每个元素的前k元素和后k元素相乘
        }
        return answer;
    }
};

时间复杂度为什么是O(n).计算如下:
初始化左右数组各为n,后续进行计算为n,加起来为3n,大O表示法忽略前常数于是得到O(n).
但是,这段代码有问题
原因在于:

for(uint64_t j = nums.size() - 2;j >= 0 ; j--)

在这里插入图片描述
可以看到初始时j的数值是没有问题的,但是当调试到j为0时,之后在进行j–后,问题就出现了:
在这里插入图片描述
在这里插入图片描述
此时可以发现j变为一个异常大的数,这会导致当前循环会无限进行,因为当j变为0后经过j–又会变成一个超级大的数。
究其原因则是:
一个 64 位无符号数的最小值是 0 ,最大值是 18446744073709551615
当对 0 进行–运算时,它会变为最大值(超出最小值 0 的下一个值)

因此,当一个 64 位无符号整数变为 0 时,对它进行–操作,实际上是一个模拟的“自增”操作。
因为它跳出 0 最小值,变为最大值 18446744073709551615。
这种行为的根本原因是:
64 位无符号数是循环复用整个取值范围的,而有符号整数不具备这种特性
无符号整数会将最高位作为值的一部分,而不是用来表示正负号
当然,这种行为只发生在 0 时。对其他值进行–操作,结果仍然是减 1。
所以在进行无符号的加减法时特别注意为0的情况。
之前在学计组的时候虽然已经知道会有这种情况的发生,但是一直没注意,今天真的是给我上了一课。
修改后的代码:

class Solution {
public:
    vector<int> productExceptSelf(vector<int>& nums) {
        vector<int> L(nums.size()),R(nums.size()),answer(nums.size());
        L[0] = 1,R[nums.size()-1] = 1;
        for(uint64_t i = 1;i<nums.size();i++){
            L[i] = L[i-1] * nums[i-1];
        }
        for(int j = nums.size() - 2;j >= 0 ; j--){
            R[j] = R[j+1] * nums[j+1];
        }
        for(uint64_t k = 0;k < nums.size();k++){
            answer[k] = L[k]*R[k];
        }
        return answer;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值