leetcode 29. 两数相除 中等 二分+快速乘 各种边界处理防止溢出

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

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

整数除法的结果应当截去(truncate)其小数部分,例如:truncate(8.345) = 8 以及 truncate(-2.7335) = -2

示例 1:

输入: dividend = 10, divisor = 3
输出: 3
解释: 10/3 = truncate(3.33333…) = truncate(3) = 3
示例 2:

输入: dividend = 7, divisor = -3
输出: -2
解释: 7/-3 = truncate(-2.33333…) = -2

提示:

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

l为什么从1而不是0开始,//l=0的话第一次就存在溢出:(r-l+1)>>1
class Solution {
    public int divide(int dividend, int divisor) {
        if(dividend==Integer.MIN_VALUE){
        	if(divisor==1)return Integer.MIN_VALUE;
        	if(divisor==-1)return Integer.MAX_VALUE;
        }
        if(divisor==0)return 0;
        int fu=1;
        if(dividend>0){
        	dividend=-dividend;
        	fu*=-1;
        }
        if(divisor>0){
        	fu*=-1;
        	divisor=-divisor;
        }
        int l=1,r=Integer.MAX_VALUE;
        while(l<r){
        	int mid=l+((r-l+1)>>1);//l=0的话第一次就存在溢出
        	if(mid==Integer.MAX_VALUE)return Integer.MAX_VALUE;//中间值可能到边界
        	boolean check=qadd(divisor,mid,dividend);
        	if(check)l=mid;
        	else r=mid-1;
        }
        if(qadd(divisor,l,dividend)==false)l=0;
        return fu*l;
    }

    public boolean qadd(int y,int z,int x){
    	//要始终保证z*y>=x;返回true
    	int ad=y;
    	int ans=0;
    	while(z>0){
    		if((z&1)==1){
    			if(ans<x-ad)return false;
    			ans+=ad;
    		}
    		if(z!=1){//z=1时不用加了,都跳出循环了
    			//防止ad溢出,同时做判断
    			if(ad<x-ad)return false;
    			ad+=ad;
    		}
    		z>>=1;
    	}
    	return true;
    }
}
另一种二分模板
class Solution {
    public int divide(int dividend, int divisor) {
        if(dividend==Integer.MIN_VALUE){
        	if(divisor==1)return Integer.MIN_VALUE;
        	if(divisor==-1)return Integer.MAX_VALUE;
        }
        int fu=1;
        if(dividend>0){
        	dividend=-dividend;
        	fu*=-1;
        }
        if(divisor>0){
        	fu*=-1;
        	divisor=-divisor;
        }
        int l=0,r=Integer.MAX_VALUE,ans=(int)Math.random()*Integer.MAX_VALUE;
        while(l<=r){
        	int mid=l+((r-l)>>1);
            if(mid==Integer.MAX_VALUE)return mid;//比上一种模板多了溢出判断,//因为,下面可能需要执行l=mid+1导致溢出,而第一个模板则是l=mid,不存在这种情况
        	 boolean check=qadd(divisor,mid,dividend);
        	 if(check){
        		 ans=mid;
        		 l=mid+1;
        	 }else{
        		 r=mid-1;
        	 }
        }
        return fu*ans;
    }

    public boolean qadd(int y,int z,int x){
    	//要始终保证z*y>=x;返回true
    	int ad=y;
    	int ans=0;
    	while(z>0){
    		if((z&1)==1){
    			if(ans<x-ad)return false;
    			ans+=ad;
    		}
    		if(z!=1){//z=1时不用加了,都跳出循环了
    			//防止ad溢出,同时做判断
    			if(ad<x-ad)return false;
    			ad+=ad;
    		}
    		z>>=1;
    	}
    	return true;
    }
}
posted @ 2022-11-17 23:01  林动  阅读(13)  评论(0编辑  收藏  举报