zoukankan      html  css  js  c++  java
  • leetcode 29 两数相除

    问题描述  

      给定两个整数,被除数 dividend 和除数 divisor。将两数相除,要求不使用乘法、除法和 mod 运算符。

      返回被除数 dividend 除以除数 divisor 得到的商。

      示例 1:

      输入: dividend = 10, divisor = 3
      输出: 3

      示例 2:

      输入: dividend = 7, divisor = -3
      输出: -2

      说明:

    • 被除数和除数均为 32 位有符号整数。
    • 除数不为 0。
    • 假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−231,  231 − 1]。本题中,如果除法结果溢出,则返回 231 − 1。

          这个问题涉及到了计算机如何利用逻辑运算和加减法来求得除法,这个问题之前一度困扰了我很久。

      

    	/**
    	 * 逼近
    	 * 先定符号
    	 * 结果是正是负还是0?
    	 * @param dividend
    	 * @param divisor
    	 * @return
    	 */
    	public static int divide(int dividend, int divisor) {
    		if (dividend == 0) {
    			return 0;
    		}
    		if (dividend == Integer.MIN_VALUE && divisor == -1) {
    			return Integer.MAX_VALUE;
    		}
    		if (divisor == -1) {
    			return -dividend;
    		}
    		if (divisor == 1) {
    			return dividend;
    		}
    		int res = 0, Sum = 0;
                boolean plus = false;
                //同号
                if ((dividend > 0 && divisor > 0) || (dividend < 0 && divisor < 0)) {
                  plus = true;
                }
              //将两个数都变成负数
              dividend = dividend > 0 ? ~dividend + 1 : dividend;
              divisor = divisor > 0 ? ~divisor + 1 : divisor;
    	  for (int i = 30; i > -1; i--) {
      	    //被加数未溢出  加之后的结果未溢出  加之后的结果小于被除数
    		  int addNum = divisor<<i;
    		  if (addNum>>i == divisor && Sum + addNum < 0 && Sum + addNum >= dividend) {
    			  Sum += addNum;
    			  res += plus ? 1<<i : -1<<i;
    		  }
    	  }return res;
    	}
    

      

       题解(写代码时候的奇怪想法。。。): 

        首先进行边界处理之类的。

        一开始我采用了二分法猜数字,首先做一个记号记录结果,然后把被除数(dividend)和除数(divisor)都转换为正数 (信息加工),这样结果就一定是在[0,dividend]。

        初始化left = 0, right = dividend,mid = dividend << 1;

        然后通过for循环累加mid次验证是否符合结果( mid * divisor <= dividend并无法取到比mid更大的mid'去满足前面条件);

        由于平时并不是经常使用二分碰到了以下问题:

          二分的边界问题: 

            如何写出不杂乱的代码?

            因为常常使用 mid = (left + right)<<1;

            故而遗忘了mid还可以向右偏 mid = ((left + right)<<1) + 1;。

          因为要保证结果一定在边界内,故而

            left = mid + 1; right = mid - 1;常常不能同时出现(视情况而定吧)。

          所以有时候

            采用 right = mid - 1; left = mid;这个组合时:

            mid = (left + right)<<1; (left + 1 = right) 时候回卡死!

            这个时候要mid  = ((left + right)<<1) + 1;(向右偏)

      当然这样的思路写出来的代码的结果就是我挂了。。

        for循环累加代替乘法实在太慢了!!!

      然鹅,这时候我想到了一个办法。

        divisor * mid 可以写成 divisor (m0 * 2^31 + m1 * 2^30 + m2 * 2^31 ....+m30 * 2^0)

        然后二的m次方这个东西我是可以通过左移来得到的!

        于是我兴奋地用这个方法验证mid对不对。

      发现很多边界问题无法解决

      例如:

        之前说的右偏碰到Integer.MIN_VALUE

        Integer.MIN_VALUE无法转换为正数

        mid取得太大,数据溢出,本来divisor * mid已经超过了Integer.MAX_VALUE。却还是几千。。。

      于是我处于崩溃的边缘。。。

        这样搞下去我要屎了!

      然鹅,解手的时候。我想:

        我可以把所有数都转换为负数先啊

        我可以不用猜测mid是多少啊

        我直接从一步一步逼近被除数就行啦??? 好像真的是。。

        例如 :

          结果如若为101010111...(32位)

        那么我从头开始的非符号位开始看能不能加进去就好啦! 如若能加进去就逼近了被除数,数据本身溢出,加进去溢出,加进去大于被除数就代表不能加!

        其他的都加,反正我要的也是最逼近的数。。。

  • 相关阅读:
    .vimrc
    GNU_makefile_template
    EM算法
    《设计模式之禅》学习笔记
    k-近邻算法
    机器学习基础
    《机器学习实战》学习笔记
    使用Apriori算法和FP-growth算法进行关联分析
    An ffmpeg and SDL Tutorial
    在Qt Creator中添加OpenCV库
  • 原文地址:https://www.cnblogs.com/vizdl/p/10045871.html
Copyright © 2011-2022 走看看