原题链接:https://leetcode-cn.com/problems/divide-two-integers/
解题思路
此题的思路就除法的基本过程进行模拟的一个方法:
在一个除法算式中,对于 被除数 ÷ 除数 = 商 ... 余数
,所以,我们能很快的想到 商 * 除数 + 余数 = 被除数
。
- 首先,对于本题,开始想到的方法就是将被除数减除数,看能够减多少次,那么最后的结果就是多少。但是,如果是
Integer.MIN_VALUE 除以-1
,则需要循环的次数过大,导致TLE
.
可以考虑使用位移法来解决此题目。设被除数为x
,除数为y
。 假设n
开始就是31,那么我们可以不断的去缩小n的值,并判断 (2 ^ n) * y
是否是小于等于x的,如果满足小于的条件,将2^n累加到结果中,最后将x减去对应的(2 ^ n) * y
。重复这个过程…
此方法的优化地方,就是在于利用位移的操作,将原来记录被除数能减多少个除数的过程进行了加速,及时不再是一个一个的减,而是从2^31 * 除数,2^30 * 除数 …开始减,而判断能否减去对应的 2^n * 除数,就是利用当前 被除数 是否大于 2 ^ 31 * 除数。
例子:33/1
为例子:
没有优化的方法 : 33 - 1
要减 33
次,所以最后结果输出33
.
优化之后的方法: 33 开始尝试减去 2^31 * 1
不行,依次类推2^31,..., 2^5 * 1
满足小于的情况,所以, 33 >= 2^5 * 1
。将2^5
累加到结果中,后继续运算,33 - 2 ^ 5 * 1 = 1, 被除数变成了1, 继续从2^4开始,一直到1 >= 2 ^ 0 * 1
,将2^0
累加到结果中,结束运算,所以,最后的结果是2^5 + 2^0 = 33
。
代码实现如下!
代码
JAVA代码
class Solution {
public int divide(int x, int y) {
// 如果是等于零的话,没有意义
if(y == 0) return 0;
// 判断当前可能会溢出的情况只有这个一种
if(x == Integer.MIN_VALUE && y == -1) return Integer.MAX_VALUE;
// 记录当前的正负性
boolean negative = (x ^ y) < 0;
// 由于a可能需要处理移除的问题,用long类型进行存储
long a = Math.abs((long)x);
long b = Math.abs((long)y);
int res = 0;
for(int i = 31; i >=0; i--){
// 每次判断当前是否达到除数的标准
if(a >> i >= b){
res += 1 << i;
// 减去相应的值
a -= b << i;
}
}
return negative ? -res:res;
}
}
C++ 代码
class Solution {
public:
int divide(int x, int y) {
typedef long long LL;
//将所有的值都添加到一个集合中
vector<LL> exp;
bool is_minus = false;
if(x < 0 && y > 0 || x > 0 && y < 0) is_minus = true;
// 将所得的二进制乘法放到集合中
LL a = abs((LL)x), b = abs((LL)y);
for(LL i = b; i <= a; i = i + i) exp.push_back(i);
LL res = 0;
for(int i = exp.size() - 1; i >= 0; i--)
{
if(a >= exp[i])
{
a-=exp[i];
res += 1ll << i;
}
}
if( is_minus) res = -res;
if(res > INT_MAX || res < INT_MIN) res = INT_MAX;
return res;
}
};