• 【Divided Two】cpp


    题目:

    Divide two integers without using multiplication, division and mod operator.

    If it is overflow, return MAX_INT.

    代码:

    class Solution {
    public:
        int divide(int dividend, int divisor) {
                if (divisor==0) return dividend>=0 ? INT_MAX : INT_MIN;
                if (divisor==-1 && dividend==INT_MIN) return INT_MAX;
                // record negative or positive
                int sign = 1;
                sign = dividend<0 ? -sign : sign;
                sign = divisor<0 ? -sign : sign;
                // transfer to non negative long long type & get rid of overflow
                unsigned long long _dividend = abs((long long)dividend);
                unsigned long long _divisor = abs((long long)divisor);
                if ( _dividend < _divisor ) return 0;
                // using bit manipulation to simulate binary multiply
                unsigned long step = 1;
                while ( _dividend > _divisor )
                {
                    _divisor = _divisor << 1;
                    step = step << 1;
                }
                // cout << "step:" << step << endl;
                // cout << "_divisor:" << _divisor << endl;
                unsigned long res = 0;
                while ( _dividend >= abs((long long)divisor) )
                {
                    while ( _dividend >= _divisor)
                    {
                        _dividend -= _divisor;
                        res = res + step;
                    }
                    _divisor = _divisor >> 1;
                    step = step >> 1;
                }
                return sign==1 ? res : -res;
        }
    };

    tips:

    这道题非常好,即考查了bit manipulation又考查了类型转换的细节。

    做题的过程中,学习了下面这个blog的优秀思路(http://yucoding.blogspot.sg/2013/01/leetcode-question-28-divide-two-integers.html

    1. 不让用乘法、除法、幂运算等。但还是要做除法,这个时候可以用bit manipulation。本质就是用二进制运算代替十进制运算

      比如 155/3 ,如果不让你用除法,只允许用乘法或加法,该怎么做呢?

      做法1(加法):

          3+3+...+3+3==153,result=51

          这种解法最直观,但也最耗时,时间复杂度为O(n)

      做法2(乘法+加法):

          1)3*100=300>155 , 得知result<100,10个3*10大于被除数

          2)3*10=30<155, 得知result>10 1个3*10小于被除数

             155-30-30-30-30-30==5<30, result = result +50

          3) 3*1=3<5,得知result大于一个3*1小于10个3*1

            5-3=2<3,result = result +1 = 51

          最终得到的结果是 155 = 3*5*10^1 + 3*1*10^0 + 2。

          解释如下,如果用10的幂构成的多项式(多项式每个项目前面有固定的系数就是被除数);

          并在每一项前面配上一个变化系数(加颜色),155就被表示上述的形式

    显然做法2的时间复杂度log(n)更低,按照题意的要求,乘以10这种做法肯定是不行了,所以把10换成2,改用移位运算,右移一次等于乘以2。

    比如 15/3 = 3*2^2 + 3*2^0 , 可得商为2^2+2^0=5。

    2. 这道题还有一个细节就是变量类型转换,以及涉及到边界的临界问题:

    a) INT_MIN=-2147483648

    但abs(INT_MIN) 呢?还是-2147483648。因为int的上限就是2147483647;因此如果除数被除数有一个是INT_MIN都没法变成正的啊!

    如果是 unsigned long long v = abs(INT_MIN)呢?这种的可以么?结果是:18446744071562067968,还是不对。

    好奇这个数咋来的呢?

    复习了知识点:负数在计算机中以补码形式表示

    INT_MIN的补码形式是1000....000(31个0);

    现在要把INT_MIN转成unsigned long long类型的,这中间发生了什么呢?

    1) 把有符号转无符号数,如果是负数的话,要取补码

    2) unsigned long long是64位的,INT_MIN是32位的;转换后高位要补上0

    这就涉及到顺序的问题:先取补码还是先高位补0?

    根据事实结果:先补高位的0,再取补码

    补高位0:0...0(32个零)10...000(31个零)

    取补码:a)先取反码 1...1(32个1)01...1(31个1)

        b)再对反码加1 1...1(32个1)10...0(31个0)

    这样操作之后,结果就是2^64-2^31,恰好等于18446744071562067968,就对上了。

    但是这个并不是我们需要的,我们要得到的是2147483648,怎么办呢?

    unsigned long long v = abs((long long)INT_MIN) 这样的结果是2147483648。

    这个过程是怎样的呢?

    1)先在高位补上32个0,变成了0...0(32个零)10...0(31个零)

    2)abs函数识别最高位为0,直接不变

    这样v的取值就是2^31=2147483648,对上了。

    补几个参考资料blog:

    http://www.cnblogs.com/lknlfy/archive/2013/04/02/2996320.html

    ============================================

    第二次过这道题,按照第一次的思路coding,改了几个corner case,最后AC了,代码比第一次要简洁清晰很多。

    class Solution {
    public:
        int divide(int dividend, int divisor) {
                // corner cases
                if ( divisor==0 ) return dividend>=0 ? INT_MAX : INT_MIN;
                if ( dividend==0 ) return 0;
                if ( dividend==INT_MIN && divisor==-1 ) return INT_MAX;
                int sign = 1;
                sign = divisor>=0 ? sign : -sign;
                sign = dividend>=0 ? sign : -sign;
                unsigned long long n = abs((long long)dividend);
                unsigned long long d = abs((long long)divisor);
                if ( n<d ) return 0;
                if ( n==d ) return sign;
                // bit manipulation simluate decimal
                unsigned long long ret = 0;
                unsigned long long base = 1;
                while ( base*d<n ) base = base<<1;
                while ( base>=1 )
                {
                    if ( base*d<=n )
                    {
                        ret += base;
                        n = n - base*d;
                    }
                    base = base >> 1;
                }
                return ret*sign;
        }
    };
  • 相关阅读:
    C# this关键字的四种用法
    MVC Html.AntiForgeryToken() 防止CSRF攻击
    简单的C#网络爬虫
    string format的各类格式及用法
    选取两个有序数组中最大的K个值,降序存入另一个数组中
    程序员面试:青蛙跳台阶问题(变态跳台阶)
    贪心算法,递归算法,动态规划算法比较与总结
    storm简介[ZZ]
    逻辑回归:使用SGD(Stochastic Gradient Descent)进行大规模机器学习
    mahout分类
  • 原文地址:https://www.cnblogs.com/xbf9xbf/p/4563659.html
Copyright © 2020-2023  润新知