LeetCode_String to Integer (atoi)

LeetCode第8题,字符串转整型。

  1. 为什么不用一个大循环而选择用分段式的小循环?

    合法字符串的格式是分段式的,但问题是,我们不知道实际的字符串的各个段长度为多少(无法定界)。若使用一个大循环轮询处理,需要额外的状态变量与判断语句决定当前状态,代码逻辑会变得更加复杂

  2. 如何进行溢出判断

  3. 这个问题是写本文的主要动机,我们可以很直接的得到一个可读性很高的、同时也是高赞答案采用的方式:

    if(total>Integer.MAX_VALUE/10 || 
        (total==Integer.MAX_VALUE/10 && 
            digit>Integer.MAX_VALUE%10))
        return sign == -1 ? Integer.MIN_VALUE : Integer.MAX_VALUE;
    total = total*10 + digit;
    
  4. 这种写法的一个隐藏点是:当输入为“-2147483648”时,按道理是不溢出的,但此处的代码将其视为溢出,然后由于前边读取的负号,返回了不溢出的Integer.MIN_VALUE。

  5. 那么,除此之外,有没有其它溢出判断的方式呢

  6. 笔者开始的想法如下:

    int tmp = total;
    total = total*10 + digit;
    if(tmp > total)
        return sign == -1 ? Integer.MIN_VALUE : Integer.MAX_VALUE
    
  7. 上述代码的出发点是:溢出时,一方面,total x 10 后的值要减去一个很大的整数;另一方面,有可能 total x 10 会将 Integer 的符号位置1,导致正数变负数。因此,若旧值(tmp)大于新值(total),说明出现溢出。

    顺便一提,二进制 x10 等于左移三位加左移一位(x8 + x2)。

  8. 这乍一看没问题,但测试时,发现仍有一些情况没有考虑到:溢出减值,有可能减去后的新值仍大于旧值;溢出变负,则只是部分情况,因为溢出后仍可能是正值

    举个很简单的例子:定义一个 4bit 的数据类型,2(0010)x 10 后,得到 20(0001,0100),溢出处理后得到 4(0100),4 > 2 > 0

  9. 笔者后来又想了种方式:

    total = total*10 + digit;
    if(total%10 != digit)
        return sign == -1 ? Integer.MIN_VALUE : Integer.MAX_VALUE
    
  10. 上述代码的出发点是:没溢出时,total x 10 一定得到一个整十数(也就是个位为0的数),那么加上 digit 再%10余数一定还是digit

    若 total x 10 发生了溢出,total 最大值2^31-1,x10 后溢出位只有 2^34,换句话讲,溢出减去的大整数只有—— 2^34, 2^33, 2^32, 2^34 + 2^32——这四种情况,而其对应的个位数分别为——4、2、6、8。此时,若再加上 0~9 的 digit,%10 取个位数必定不是digit若 total x 10 没有溢出,而加上 digit 后溢出,则同理,由于上限值将 digit 进行了截断,溢出后的个位数也必定不是digit,而是digit的一部分

  11. 然而测试时又出现了问题:
    在这里插入图片描述

  12. 可以看出,在所有的测试样例中,就只有这一个例子没通过。而对于该例,原本应该触发溢出返回 Integer.MAX_VALUE,但实际却输出 -20,也就是说没有触发溢出,即,我们的溢出判断语句仍有漏网之鱼

  13. 问题到底出在哪里呢?看到输出的 20,笔者好像知道了问题出在哪里,但为了严谨起见,笔者用程序穷举了本题会出现的所有情况进行测试,发现了一系列漏网之鱼:

    当 total = [1932735284,2^31-1],total x 10发生溢出,而溢出后皆为负值且是整十数,故若恰巧digit = 0,溢出判断语句 total%10 != digit 失效

    有意思的是,区间 [1932735284,2^31-1] 共 214748364 个整数,区间每个数 x10 后溢出对应的负整数为 [-2147483640,…,-30,-20,-10],恰为 Interger 能表示的所有负整十数

  14. 那么,为什么会这样呢?笔者在仔细分析了一些代表性数据溢出的过程后,得出如下结论:

    1. 若 total x 10 的低 32 位仍为正数,则 total%10 != digit 可以进行溢出判断,原因如第10条所述;

    2. 若 total x 10 的低 32 位变为负数,则由于上限值的个位数非0,故在(long)total x 10 减去大整数后,仍有可能得到一个(int)的负整十数,导致溢出判断无效;

    3. 那么,可以将两种方法相结合,用如下代码进行溢出判断

      int tmp = total;
      total = total*10 + digit;
      if(total%10 != digit || tmp > total)
          return sign == -1 ? Integer.MIN_VALUE : Integer.MAX_VALUE;
      
    4. 评论区又找到一种方式,思路是逆运算判等,不过同样有边界情况需要打补丁:total = 214748364,digit = 8:

      int tmp = total;
      total = total*10 + digit;
      if((total-digit)/10 != tmp || total < 0)
          return sign == -1 ? Integer.MIN_VALUE : Integer.MAX_VALUE;
      
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值