【leetcode】【剑指offer Ⅱ】001. 整数除法

问题描述:

  • 给定两个整数 ab,求它们的除法的商 a/b,要求不得使用乘号、除号以及求余符号。
    • 整数除法的结果应当截去其小数部分。【即向下取整】
    • 假设环境只能存储 32 位有符号整数,其数值范围是 [ − 2 31 , 2 31 − 1 ] [−2^{31}, 2^{31}−1] [231,2311]
    • 如果除法结果溢出,则返回 2 31 − 1 2^{31}−1 2311

核心思路:

  • 最朴素的方法是很直观的,首先假设 ab 均为正整数,一直使用除数 a 减去被除数 b,直到 a < b,其中记录并更新进行减操作的次数,相当于以步进的方式更新商:
    int ans = 0; // 商
    while(a >= b)
    {
      a -= b;
      ++ans;
    }
    
  • 但步进的实现只能通过部分案例,因为假若分子分母相差较大,则很容易超时,此时需要考虑如何加快搜索,而利用快速乘的思想(类似于快速幂)可以对算法进行优化。
    • 优化后不再采取步进的方式,而是通过倍增被除数 b 来找到商:
    int ans = 0; // 最终的商
    while(a >= b)
    {
      int tmp = b; // 被除数需要倍增,所以用一个新的变量来进行更新
      int c = 1; // 当前的商,用来倍增作记录,后续会更新到ans中去
      while(tmp+tmp <= a)
      {
        tmp += tmp; // tmp <<= 1
        c += c; // c <<= 1
      }
      a -= tmp;
      ans += c;
    }
    
  • 目前的思路只考虑 ab 均为正整数的情况,且没有考虑很多的溢出情况,后续只需要对所有可能溢出的地方进行处理即可。【但通常处理溢出都很繁琐】
  • 后续代码实现需要注意的地方有:
    1. ab 均置为负数,是因为 INT_MIN 的绝对值要比 INT_MAX 更大,因而为了避免将 INT_MIN 取反导致溢出,则将两个数置为负数,最后再将结果添上负号更方便。【取为负数后,前面的思路很多地方需要修改,如 while(a >= b) 需要改为判断 while(a <= b)
    2. 在 while 循环中需要判断 tmp <= INT_MIN >> 1,避免 tmp + tmp 溢出。
    3. 还要注意在代码中 ansc 均为 unsigned,这是因为 leetcode 中带的 C++ 编译器不允许 int 溢出,如果溢出会直接报错。【这里倍增 c 时产生的溢出对于题目来说是允许的】

代码实现:

class Solution
{
private:
    int INT_LIMIT = INT_MIN >> 1; // -1073741824
public:
    int divide(int a, int b)
    {
        if(a == INT_MIN and b == -1) return INT_MAX;
        bool neg = ((a >> 31) ^ (b >> 31)) != 0; // 位运算确定符号
        if(a > 0) a = -a;
        if(b > 0) b = -b;
        unsigned ans = 0;
        while(a <= b)
        {
            int tmp = b; // 当前除数
            unsigned c = 1; // 当前商
            while(tmp >= INT_LIMIT and a <= tmp + tmp) // 快速乘法
            {
                tmp += tmp;
                c += c;
            }
            a -= tmp; // 求得a剩余部分
            ans += c; // 累计商
        }
        return neg ? -ans : ans;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值