题目描述
给定两个整数,被除数 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 位有符号整数,其数值范围是 [−2^31, 2^31 − 1]。本题中,如果除法结果溢出,则返回 2^31 − 1。
题目理解
- 可以使用long型
- 不可以使用long型
溢出的情况
* 1. 两数相加,如负数 + 负数, 正数 + 正数
* 2.两数相除,如-2^31 / -1 答案为 2^31次方,产生溢出,
* 如何判断两数为异号?
* 为1说明异号,为零说明同号
* int symbol = (dividend ^ divisor) >>> 31;
* 如何解决溢出?将int型转换为long型
暴力超时
x / y 说明 y * n >= x ,将y累加,如果大于x,得到结果
public static int divide(int dividend, int divisor) {
int symbol = (((dividend ^ divisor) >>> 31) & 1) == 1 ? -1 : 1;
long x = Math.abs((long) dividend);
long y = Math.abs((long) divisor);
long res = 0;
//不采用累加,加法会溢出,采用相减的方式进行
while (x >= y) {
x -= y;
res++;
}
res = symbol == 1 ? res : -res;
//处理溢出
if (res > Integer.MAX_VALUE || res < Integer.MIN_VALUE) {
return Integer.MAX_VALUE;
}
return (int) res;
}
二分查找 + 快速乘
快速乘是快速幂的变形,此处附上快速幂模版快速幂模版
public static int divide3(int dividend, int divisor) {
int symbol = (((dividend ^ divisor) >>> 31) & 1) == 1 ? -1 : 1;
long x = Math.abs((long) dividend);
long y = Math.abs((long) divisor);
long res = 0;
if (x < y) {
return 0;
}
long left = 1;
long right = x;
long mid;
while (left <= right) {
mid = ((right - left) >> 1) + left;
long ans = quickAdd(y, mid);
if (ans == x) {
res = mid;
break;
} else if (ans > x) {
right = mid - 1;
} else {
left = mid + 1;
}
}
res = res == 0 ? right : res;
res = symbol == 1 ? res : -res;
if (res > Integer.MAX_VALUE || res < Integer.MIN_VALUE) {
return Integer.MAX_VALUE;
}
return (int) res;
}
/**
* 快速加法,m * n
*
* @param m
* @param n
* @return
*/
public static long quickAdd(long m, long n) {
long res = 0;
while (n > 0) {
if ((n & 1) == 1) {
res += m;
}
m += m;
n >>= 1;
}
return res;
}
二分查找变形,查找右边界
public static int divide3(int dividend, int divisor) {
int symbol = (((dividend ^ divisor) >>> 31) & 1) == 1 ? -1 : 1;
long x = Math.abs((long) dividend);
long y = Math.abs((long) divisor);
long res = 0;
if (x < y) {
return 0;
}
long left = 1;
long right = x;
long mid;
while (left < right) {
//注意此处要 + 1,+ 1的目的是为了找到右边界
mid = (right + left + 1) >> 1;
long ans = quickAdd(y, mid);
//二分查找变形,查找右边界
if (ans <= x) {
left = mid;
} else {
right = mid - 1;
}
}
res = symbol == 1 ? right : -right;
if (res > Integer.MAX_VALUE || res < Integer.MIN_VALUE) {
return Integer.MAX_VALUE;
}
return (int) res;
}
同理,查找左边界
注意:左边界和右边界的区别在于找到的右边界一定<=x,而左边界可能=x,也可能>x
public int divide(int dividend, int divisor) {
int symbol = (((dividend ^ divisor) >>> 31) & 1) == 1 ? -1 : 1;
long x = Math.abs((long) dividend);
long y = Math.abs((long) divisor);
long res = 0;
if (x < y) {
return 0;
}
long left = 1;
long right = x;
long mid;
while (left < right) {
mid = (right + left) >> 1;
long ans = quickAdd(y, mid);
//二分查找变形
if (ans >= x) {
right = mid;
} else {
left = mid + 1;
}
}
//查找左边界需要加上判断,为什么要加上判断?因为不一定存在right 使 y * right == x,如果一定存在则不用判断
if(quickAdd(y,right) > x){
right--;
}
res = symbol == 1 ? right:-right;
if (res > Integer.MAX_VALUE || res < Integer.MIN_VALUE) {
return Integer.MAX_VALUE;
}
return (int) res;
}
方法三:不使用long型,类快速加运算
来自于leetcode作者:AC_OIer思路,采用倍增思想,x / y,目的在于寻找x中包含几个y,那么可以采用倍增思路,x包含一个y,那么x是否包含两个y?是否包含四个y?
dividend = 39 divisor = 2
39 - 32,相当于减去了16个2
dividend = 7 divisor = 2
7 - 4,相当于减去了2个2
dividend = 3 divisor = 2
3 - 2,相当于减去了1个2
得到答案19
class Solution {
public static int divide(int a, int b) {
int min = Integer.MIN_VALUE;
int max = Integer.MAX_VALUE;
//提前处理溢出
if (a == min && b == -1) return max;
//防止溢出
int limit = min >> 1;
//是否同号
boolean flag = false;
if (a > 0) {
flag = !flag;
a = -a;
}
if (b > 0) {
flag = !flag;
b = -b;
}
int res = 0;
while (a <= b) {
int temp = b;
int count = 1;
while (temp >= limit && temp >= a - temp) {
temp += temp;
count += count;
}
res += count;
a -= temp;
}
return flag ? -res : res;
}
}