LeetCode(29)Divide Two Integers

题目内容

Divide two integers without using multiplication, division and mod operator. 求出两个数相除的商,禁用乘法或者除法或者取模运算。

题目分析

最容易的想到的办法,是把除法转化为减法,就像把乘法转化为加法一样,提交后发现,这个做法超时了,比如遇到2147483647/3这种时候,被除数很大,除数很小,基本都会超时。所以应该要使用一个特别的方法来提高效率。我想应该是位运算了。但是不知该如何写。参考了一下官网答案。发现实在很巧妙。如下,我把它微微改了一下,更好理解一些:

//官网答案,我的微改版
class Solution {
public:
    int divide(int dividend, int divisor) {
        long long a=abs(dividend); //(long long) dividend中的(long long)是为了防止-2147483648带来的溢出,下行同理。
        long long b=abs(divisor);
        int res=0;
        while(a>=b){
            long long c=b;
            for(int i=0;c<=a;i++){
                a-=c;
                res+=1<<i;
                c=c<<1;
            }
        }
        return ((dividend^divisor)>>31) ? (int)(-res) : (int)(res);//判断是否为负数 
    }
};

//官网实际答案
class Solution {
public:
    int divide(int dividend, int divisor) {
        long long a = abs((double)dividend);
        long long b = abs((double)divisor);
        
        long long ret = 0;
        while (a >= b) {
            long long c = b;
            for (int i = 0; a >= c; ++i, c <<= 1) {
                //std::cout<<"1-1 a="<<a<<", c="<<c<<" ,i="<<i<<" ,ret="<<ret<<std::endl;
                a -= c;
                ret += 1 << i;
                //std::cout<<"1-2 a="<<a<<", c="<<c<<" ,i="<<i<<" ,ret="<<ret<<std::endl;
            }
        }
        return ((dividend^divisor)>>31) ? (int)(-ret) : (int)(ret);
    }
};

这个算法要理解起来,只需明白它提速的基本原理。
现在用两个例子谈谈它的基本原理。基本原理是,dividend非常大,所以把它切割成几块,一块一块地和divisor计算商,再把这些商都加起来就可以了。在切割的时候,各个小块是不均匀的。第1块的大小正好是divisor的大小,第2块的大小正好是divisor扩大2倍的大小,第3块的大小正好是divisor扩大2²倍的大小,第4块的大小正好是divisor扩大2³倍的大小......以此类推,直到把dividend分割完毕。以计算 49/7 = ?  为例子来看看这个过程。把上面代码中的注释恢复。可以看到下面的结果

1-1 a=49, c=7 ,i=0 ,ret=0
1-2 a=42, c=7 ,i=0 ,ret=1
1-1 a=42, c=14 ,i=1 ,ret=1
1-2 a=28, c=14 ,i=1 ,ret=3
1-1 a=28, c=28 ,i=2 ,ret=3
1-2 a=0, c=28 ,i=2 ,ret=7
49 / 7 = 7


c就是每次切割的小块,a就是每次拿走一小块之后a还剩下的数。

上面这个例子展示了正好整除的情况,那么其实还有不整除的情况,这样切出的小块中,最后一块是没法进入for循环中的,这时while循环就起作用了,来让最后单出来的一块也参与运算,直到a<b时停止,此时a被分割结束了,a/b=0不再对商的累加做贡献了。用64/7 = ?这个例子来看看。结果如下

1-1 a=64, c=7 ,i=0 ,ret=0
1-2 a=57, c=7 ,i=0 ,ret=1
1-1 a=57, c=14 ,i=1 ,ret=1
1-2 a=43, c=14 ,i=1 ,ret=3
1-1 a=43, c=28 ,i=2 ,ret=3
1-2 a=15, c=28 ,i=2 ,ret=7
1-1 a=15, c=7 ,i=0 ,ret=7
1-2 a=8, c=7 ,i=0 ,ret=8
1-1 a=8, c=7 ,i=0 ,ret=8
1-2 a=1, c=7 ,i=0 ,ret=9
64 / 7 = 9


另外,一开始我的代码是这样的,超时的代码

//我的超时版代码
        int divide1(int dividend, int divisor) {
        if (divisor==0||dividend==0)
            return 0;
        if(divisor==1)
            return dividend;
        else if(divisor==-1)
            return -dividend;
        int negative_flag=0;
        if((dividend<0)&&(divisor>0)){
            dividend=-dividend;
            negative_flag=1;
        } else if((dividend>0)&&(divisor<0)) {
            divisor=-divisor;
            negative_flag=1;
        }else if((dividend<0)&&(divisor<0)){
            divisor=-divisor;
            dividend=-dividend;
        }
        int count=0;
        while(dividend>=divisor){
            count++;
            dividend-=divisor;
        }
        count=(negative_flag==1)?(-count):(count);
        return count;
    }

小结:

(1) for循环的条件体写法学习了,初始条件用i表示,结束条件用a表示。原来还可以初始条件和结束条件用不同的变量来表示。


update : 2015-01-04

虽然大概知道上面那个写法的原理,但是真正自己写出来时,还是要经过一翻调试和egd case的检验。

//100ms
class Solution {
public:
    int divide(int dividend, int divisor) {    //eg. 100 / 3 = 33;
        if (dividend == 0 || divisor == 0) return 0;
        if (dividend == INT_MIN && divisor == -1) return INT_MAX;
        if (divisor == 1) return dividend;
        long long l_dividend = abs((long long)dividend);
        long long l_divisor = abs((long long)divisor);
        // 用long long, 否则int a = abs(INT_MIN) = INT_MIN;
        long long a = l_dividend;
        long long b = l_divisor;
        long long res = 0;
        long long each = 1;
        while (a >= b) { //如果a < b,可以return0.
            //std::cout<<"a = "<<a<<", b = "<<b<<", res = "<<res<<", each = "<<each<<std::endl;
            while (a >= b) {
                //std::cout<<"b="<<b<<", res = "<<res<<"each = "<<each<<std::endl;
                each = each<<1;
                b = b <<1;
            }
            res += (each>>1);
            a -= (b>>1);
            each = 1;
            b = l_divisor;
        }
    return ((dividend ^ divisor)>>31) ? -res:res; //异或
    }
};




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值