剑指 Offer II 001. 整数除法


题目

给定两个整数 a 和 b ,求它们的除法的商 a/b ,要求不得使用乘号 ‘*’、除号 ‘/’ 以及求余符号 ‘%’ 。

注意:

整数除法的结果应当截去(truncate)其小数部分,例如:truncate(8.345) = 8 以及 truncate(-2.7335)= -2 假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−231, 231−1]。本题中,如果除法结果溢出,则返回 231 − 1

示例 1:

输入:a = 15, b = 2
输出:7
解释:15/2 = truncate(7.5) = 7

示例 2:

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

示例 3:

输入:a = 0, b = 1
输出:0

示例 4:

输入:a = 1, b = 1
输出:1

提示:

-231 <= a, b <= 231 - 1
b != 0

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/xoh6Oh


一、减法代替除法

在看到这题时,我们最容易想到的就是利用减法代替除法。

例如a=11,b=3;

while(a>=b){
	a-=b;
	res++;
}

在考虑正负数和边界之后变为这样:

public int divide(int a, int b) {
        //sign为0时代表负数,1为正数
        int res=0;int sign=0;
        // 当a为Inger.MIN_VALUE&&b==-1时,res将会越界,因为绝对值Integer.MIN_VALUE要比Integer.MAX_VALUE大
        //题目要求如果除法溢出则返回Integer.MAX_VALUE
        if(a==Integer.MIN_VALUE&&b==-1) return Integer.MAX_VALUE;
        if(a<0&&b<0){
            sign=1;
        }else if(a<0){
            b=-b;
        }else if(b<0){
            a=-a;
        }else{
            a=-a;
            b=-b;
            sign=1;
        }
        while(a<=b){
            a-=b;
            res++;
        }
        return sign==0 ? -res:res;

在这里插入图片描述

时间复杂度:O(n)。。。
力扣测试偶尔会超时。

二、位运算

第一种方法答案每次只能加1,效率太低了。我们能不能找到一个b的倍数且这个倍数接近于a值的大小。这样子每次a减去这个b的倍数得到新的被除数,而答案res则把这个记录下来,这样依次计算从而得到最终结果。

例如 a =14,b=3。
b 的倍数且接近于 a 的数为 12。
那么 res的计算就是res+=1<<2;
(上面例子的倍数刚好是2的幂次方:22

例如 a=16,b=3.
因为只能是2的幂次方倍数,这时候依然最接近a的数是3的22 倍 即12。
a-12 = 4;res+=1<<2;
再 b的倍数且接近于a的数就只剩3了,也就是3 的 20
a-3=1;res+=1<<0;
结果res就等于res=4+1=5

这样口算搁谁谁都会,如何确定倍数呢?
再解决这个问题前我们还需要考虑到另一个问题,咱们是通过b左移:b<< i 来确定倍数的, 那么会不会有b左移后出现越界也就是b大于MAX_VALUE的情况发生呢?
答案肯定会有的,首先b的值你不清楚,再一个因为你要找最接近a的值,那么肯定得尽可能最大的左移。
在这种情况下找这个数就有点麻烦(也可以)。

我们可以换另一种思维:
把 b << i 改成 a >> i
我们之前是通过找 b 的 2 i 倍的数。
现在是找a 的最大因子,这个因子为 2 i ,而a 除以 2 i 最接近 b。
而右移我们可以直接初始化 i =31,i–遍历。

代码如下:

public int divide(int a, int b) {
        int res=0;int sign;
        if(a==Integer.MIN_VALUE&&b==-1) return Integer.MAX_VALUE;
        //当b等于最小值时,除非a==b,否则结果都为0
        if(b==Integer.MIN_VALUE) return a==b? 1:0;
		//因为下面会进行取绝对值运算
		//而当a为最小值时转化成正数会越界
		//所以先减去一个正数b,再结果+1
        if(a==Integer.MIN_VALUE){
            a+=Math.abs(b);
            res++;
        } 
		//异或运算,只有当两边分别为0与1时才为1
        sign = (a>0)^(b>0) ? -1:1;
        a=Math.abs(a);b=Math.abs(b);
		//从31开始,找到最大得因子
        for(int i=31;i>=0;i--){
            if((a>>>i)-b>=0){
                a-=b<<i;
                //控制当res要大于最大值时,输出最小值
                if(res>(Integer.MAX_VALUE-(1<<i))) return Integer.MIN_VALUE;
                res+=(1<<i);
            }
        }
        return sign==1 ? res:-res;
    }

在这里插入图片描述


侵删

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值