Divide Two Integers不使用乘除法来计算两个数相除

  • 题目描述
    这里写图片描述

  • 分析
    不使用乘除法来计算两个数相除,那就从除法的定义出发:商表示的是被除数里包含了多少个除数。那最直接的一种办法,就是循环减除数,累加次数,直到不能再减。这么粗暴的办法,很容易会超时,举个极端的例子,若被除数是INT_MAX即2147483647,除数是1,那么就得减2147483647次,程序才能运行结束。

    所以,我们应该每次减多一点,我第一想到的便是位操作。因为,位操作可以看成是特殊的乘除法,如向左移一位,便是翻倍(即乘2);向右移一位,便是减半(即除2)。举个简单的例子来对照看,3的二进制是11,6的二进制是110,12的二进制是1100,…

    所以,

    核心思想
    被除数
    =除数商*
    =除数(2^0 * q0 + 2^1 q1 + 2^2 *q2 + 2^3*q3 + ….. + 2^n*qn)
    其中,q0~qn的取值要么为1,要么为0.

    举例1,
    75
    =5*15
    =5*(2^0 *1 + 2^1 *1 + 2^2 *1 + 2^3*1)

    举例2,
    15
    =3*5
    =3*(2^0 *1 + 2^1 *0 + 2^2 *1)

    代码的编写思路如下,
    1、确定n值。
    每次翻倍的累加除数(1,2,4,8,16,…倍),看直到多少倍(n),除数会大于被除数,同时记下除数被累加的次数和当前的移位数(即倍数)。
    2、确定q0~q(n-1)的值,确认是0还是1,同时更新累加次数
    3、根据除数和被除数的符号,确定商的符号

  • 代码实现

int divide(int dividend, int divisor) {
    //溢出判定
    if(divisor==0 || (dividend==INT_MIN && divisor==-1))
        return INT_MAX;

    //符号确定
    bool sign=0;
    if((dividend>0&&divisor>0)||(dividend<0&&divisor<0)){
       sign=1;
    }

    long ldividend=labs(dividend);
    long div=labs(divisor);
    long count=1;
    int bits=0;  //bits为移的位数

    //确定指数级翻倍的最大翻倍数(多项式的次数)
    while(div<=ldividend){
       count=count<<1;
       div=div<<1;
       bits++;
    }
    div=div>>1;
    bits--;
    count=count>>1;

    //确定多项式形式的其他位的值(是1还是0)
    int i;
    long copyd;
    for(i=bits-1;i>=0;i--){
        copyd=labs(divisor);
        div=div+(copyd<<i);    //<<的优先级小于+,要加括号
        //除数可以再加上copyd<<i,也即可以再累加上1<<i次
        if(div<=ldividend){
            count=count+(1<<i);
        }
        //不能加,尝试失败,除数的值还原到刚才未加时候的值
        else{
            copyd=labs(divisor);
            div=div-(copyd<<i);
        }
    }

    if(sign)
        return count;
    else
        return -count;
}
  • 遗留现象
    1、上述代码是在以C++的形式下通过的,若以C的形式是过不了的。

    2、另外一个奇怪的现象是,上述代码在eclipse上运行,有些测试集的结果会和leetcode运行的不一样。
    这里写图片描述图1:leetcode编程环境下的测试截图

    这里写图片描述
    图2:eclipse C++的测试截图

    图1和图2使用的是相同的代码,见上文。然而测试结果不一样。在eclipse中debug会发现,即使是使用labs或者fabs先强制转换类型后来取绝对值(long temp=(long)dividend; long ldividend=labs(temp);),并使用long或double型来存储,-2147483648的绝对值依然是-2147483648,这也就是造成结果不对的原因。那为什么在leetcode的编程环境下运行就不会出现这种问题呢?我暂时还不清楚,知道的亲欢迎留言告诉我。

  • 后续
    在文章的开头,我说到直接单纯的一次一次的累加,可能会超时。那如果减少一半的累加次数,还会超时吗?于是我便写了下面的代码,与上文代码不同的地方是在确定指数级翻倍的最大翻倍数之后,采取的是简单的逐次循环累加统计次数。

    下面的代码提交,就有时候会Accept,有时候会提示Time limit Exceeded。所以,最好还是用上文代码的方法,一定不会超时。

class Solution
{
public:
    int divide(int dividend, int divisor) {
    if(divisor==0 || (dividend==INT_MIN && divisor==-1))
        return INT_MAX;

    bool sign=0;
    if((dividend>0&&divisor>0)||(dividend<0&&divisor<0)){
       sign=1;
    }

    long ldividend=labs(dividend);
    long div=labs(divisor);
    long count=1;
    int bits=0;  //bits为移的位数

    if(div>ldividend)
        return 0;

    while(div<=ldividend){
       count=count<<1;
       div=div<<1;
       bits++;
    }

    div=div>>1;
    bits--;
    count=count>>1;

    long copyd=labs(divisor);;
    while(div<=ldividend){
       div=div+copyd;
       count++;
    }
    if(div!=ldividend)
        count--;

    if(sign)
        return count;
    else
        return -count;
}

};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值