• 29. Divide Two Integers


    题目:

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

    If it is overflow, return MAX_INT.

    链接: http://leetcode.com/problems/divide-two-integers/

    题解:

    最近在按照leetcode序号从小到大做题。刚做完strStr又要做这个,之后还有好几道hard,真的很恶心...不过对自己CS知识技巧和理解力不足的认识又上升到一个新的高度。看到这题首先想到的就是看答案,发现很多答案都使用long来解决溢出,碰到严格的面试官可能会吃亏,自己面微软时就吃过,所以我对自己的要求是 - 即使写得蠢笨,也不可以cheat。

    核心是判断符号后使用bit shift以及减法。做题过程中要仔细处理各种corner case。比如:

    •   divisor = 0
    •   dividend 或者 divisor = Integer.MIN_vALUE
      • divisor = Integer.MIN_VALUE
        • if dividend  = Integer.MIN_VALUE,  return 1
        • return 0
      • dividend = Integer.MIN_VALUE
        • if divisor = -1, return Integer.MAX_VALUE
        • if divisor > 0,  set divisor = - divisor   -   calculate everything in the range of negative int 

    Time Complexity - O(1), Space Complexity - O(1)。  

    public class Solution {
        public int divide(int dividend, int divisor) {
            if(divisor == 0)                        //corner case:   divisor = 0
                return Integer.MAX_VALUE;
            boolean isNeg = false;                  //get sign of result
            if((dividend < 0 && divisor > 0) || (dividend > 0 && divisor < 0))
                isNeg = true;
            int res = 0;                            //result
            if(dividend != Integer.MIN_VALUE && divisor != Integer.MIN_VALUE) {
                dividend = Math.abs(dividend);
                divisor = Math.abs(divisor);
                
                for(int i = 0; i < 32; i++) {
                    res <<= 1;
                    if((dividend >> (31 - i)) >= divisor) {       // shift right dividend and compare if >= divisor
                        dividend -= (divisor << (31 - i));
                        res++;
                    }
                }
            } else {                                              
                if(divisor == Integer.MIN_VALUE)
                    return dividend == Integer.MIN_VALUE ? 1 : 0;   //corner case: divisor = Integer.MIN_VALUE
                
                if(divisor == -1)                                   //corner case: Integer.MIN_VALUE / -1
                    return Integer.MAX_VALUE;
                
                if(divisor > 0)                                     //calculate result in negative integer range
                    divisor = -divisor;                         
                
                for(int i = 0; i < 32; i++) {
                    res <<= 1;
                    if((dividend >> (31 - i)) <= divisor) {         // shift right dividend and compare if <= divisor
                        dividend -= (divisor << (31 - i));
                        res++;
                    }
                }    
            }
            
            return isNeg ? -res : res;
        }
    }

    另外的一个想法由于技术太渣没验证过, 来自本科学过的电路知识。在学CRC冗余码时好像学过一种技术叫长除法,可以计算两个binary array相除的结果。可以使用库函数Integer.toBinaryString把两个input int转换为32-bit String,然后利用类似的减,shift,减, shift来得到结果和余数。有机会要验证试试。 心得是后悔没学好电子工程...有很多知识和技巧可以和CS结合起来,比如LSD, MSD, 真值表,卡诺图,run-length,Huffman,FFT, DCT, 加法器减法器乘法器除法器等等。在新的领域继续努力吧。

    Follow up 可以是不用四则运算符号求加法,减法,乘法和除法。要好好看一看Computer Arithmetic Algorithms

    加法:

    public int add(int a, int b) {
        while(b != 0) {
            int carry = a & b;      //carry
            a = a ^ b;              //sum
            b = carry << 1;
        }
        
        return a;
    }

    减法:

    pubic int minus(int a, int b) {
        return plus(a, plus(~b, 1));
    }

    乘法:

    也比较麻烦,待补充

    除法:

    二刷:

    Java:

    本来尝试优化旧程序,把dividend和divisor全部转化为负数然后计算。结果发现负数的位移操作比较复杂。比如 -5 >> 10 = -1,  -5 >> 3 = -1,  -5 >> 2 = -2。最小也就是-1了。所以最后还是用老办法来写。希望三刷的时候能够找到这个巧妙规律。

    public class Solution {
        public int divide(int dividend, int divisor) {
            if (divisor == 0) {
                return Integer.MAX_VALUE;
            }
            boolean hasSameSign = (dividend >= 0) ^ (divisor < 0); 
            int res = 0;
            if (dividend != Integer.MIN_VALUE  && divisor != Integer.MIN_VALUE) {
                dividend = Math.abs(dividend);
                divisor = Math.abs(divisor);
                for (int i = 0; i < 32; i++) {
                    res <<= 1;
                    if ((dividend >> (31 - i)) >= divisor) {
                        dividend -= (divisor << (31 - i));
                        res++;
                    }
                }
            } else {
                if (divisor == -1) {
                    return Integer.MAX_VALUE;
                }
                if (divisor == Integer.MIN_VALUE) {
                    return dividend == divisor ? 1 : 0;
                }
                if (divisor > 0) {
                    divisor = -divisor;
                }
                for (int i = 0; i < 32; i++) {
                    res <<= 1;
                    if ((dividend >> (31 - i)) <= divisor) {
                        dividend -= (divisor << (31 - i));
                        res++;
                    }
                }
            }
            return hasSameSign ? res : -res;
        }
    }

    三刷:

    需要更熟练bit manipulation和比较,以及边界条件。

    Java:

    public class Solution {
        public int divide(int dividend, int divisor) {
            if (divisor == 0) return Integer.MAX_VALUE;
            if (dividend == 0) return 0;
            boolean hasSameSign = (dividend > 0) ^ (divisor < 0);
            int res = 0;
            if (dividend != Integer.MIN_VALUE && divisor != Integer.MIN_VALUE) {
                dividend = Math.abs(dividend);
                divisor = Math.abs(divisor);
                for (int i = 0; i < 32; i++) {
                    res <<= 1;
                    if ((dividend >> (31 - i)) >= divisor) {
                        dividend -= (divisor << (31 - i));
                        res++;
                    }
                }
            } else {
                if (divisor == Integer.MIN_VALUE) return dividend == divisor ? 1 : 0;
                if (divisor == -1) return Integer.MAX_VALUE;
                if (divisor > 0) divisor = -divisor;
                for (int i = 0; i < 32; i++) {
                    res <<= 1;
                    if ((dividend >> (31 - i)) <= divisor) {
                        dividend -= (divisor <<(31 - i));
                        res++;
                    }
                }
            }
            return hasSameSign ? res : -res;
        }
    }

    Update:

    上面的 res <<= 1这些运算比较晦涩。

    其实就是把除法结果变成 2的第i个bit位的减法。就像  15 = 20 + 21 + 22 + 2一样。  

    注意在dividend = Integer.MIN_VALUE的时候要特殊处理,这时候我们把divisor也变为负的,然后使用类似正数情况下的 shift and minus就可以了。

    public class Solution {
        public int divide(int dividend, int divisor) {
            if (divisor == 0) return Integer.MAX_VALUE;
            if (dividend == 0) return 0;
            boolean sameSign = (divisor > 0) ^ (dividend < 0);
            int res = 0;
            if (divisor != Integer.MIN_VALUE && dividend != Integer.MIN_VALUE) {
                divisor = Math.abs(divisor);
                dividend = Math.abs(dividend);
                for (int i = 0; i < 32; i++) {
                    if ((dividend >> (31 - i)) >= divisor) {
                        dividend -= (divisor << (31 - i));
                        res += (1 << (31 - i));
                    }
                }
            } else {
                if (divisor == Integer.MIN_VALUE) return dividend == Integer.MIN_VALUE ? 1 : 0;
                if (divisor == -1) return Integer.MAX_VALUE;
                if (divisor > 0) divisor = -divisor;
                for (int i = 0; i < 32; i++) {
                    if ((dividend >> (31 - i)) <= divisor) {
                        dividend -= (divisor << (31 - i));
                        res += (1 << (31 - i));
                    }
                }
            }
            return sameSign ? res : -res;
        }
    }

    Reference:

    https://leetcode.com/discuss/26270/no-use-of-long-java-solution

  • 相关阅读:
    kettle的使用(ETL,数据仓库技术)
    最近工作用到压缩,写一个zip压缩工具类
    JAVA 7新特性——在单个catch代码块中捕获多个异常,以及用升级版的类型检查重新抛出异常
    JAVA反射机制
    guava的使用
    web项目中无法开启或404
    关于request.getServletContext()方法报错的问题
    使用U盘给笔记本重做系统
    sed速查手册
    awk速查手册
  • 原文地址:https://www.cnblogs.com/yrbbest/p/4435159.html
Copyright © 2020-2023  润新知