题目
给定两个整数 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;
}
侵删