LeetCode(29)Divide Two Integers

来源:互联网 发布:淘宝新店怎么提升销量 编辑:程序博客网 时间:2024/06/05 17:50

题目内容

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的检验。

//100msclass 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; //异或    }};




0 0
原创粉丝点击