题目描述
分析
不使用乘除法来计算两个数相除,那就从除法的定义出发:商表示的是被除数里包含了多少个除数。那最直接的一种办法,就是循环减除数,累加次数,直到不能再减。这么粗暴的办法,很容易会超时,举个极端的例子,若被除数是INT_MAX即2147483647,除数是1,那么就得减2147483647次,程序才能运行结束。所以,我们应该每次减多一点,我第一想到的便是位操作。因为,位操作可以看成是特殊的乘除法,如向左移一位,便是翻倍(即乘2);向右移一位,便是减半(即除2)。举个简单的例子来对照看,3的二进制是11,6的二进制是110,12的二进制是1100,…
所以,
核心思想:
被除数
=除数商*
=除数(2^0 * q0 + 2^1 q1 + 2^2 *q2 + 2^3*q3 + ….. + 2^n*qn)
其中,q0~qn的取值要么为1,要么为0.举例1,
75
=5*15
=5*(2^0 *1 + 2^1 *1 + 2^2 *1 + 2^3*1)举例2,
15
=3*5
=3*(2^0 *1 + 2^1 *0 + 2^2 *1)代码的编写思路如下,
1、确定n值。
每次翻倍的累加除数(1,2,4,8,16,…倍),看直到多少倍(n),除数会大于被除数,同时记下除数被累加的次数和当前的移位数(即倍数)。
2、确定q0~q(n-1)的值,确认是0还是1,同时更新累加次数
3、根据除数和被除数的符号,确定商的符号代码实现
int divide(int dividend, int divisor) {
//溢出判定
if(divisor==0 || (dividend==INT_MIN && divisor==-1))
return INT_MAX;
//符号确定
bool sign=0;
if((dividend>0&&divisor>0)||(dividend<0&&divisor<0)){
sign=1;
}
long ldividend=labs(dividend);
long div=labs(divisor);
long count=1;
int bits=0; //bits为移的位数
//确定指数级翻倍的最大翻倍数(多项式的次数)
while(div<=ldividend){
count=count<<1;
div=div<<1;
bits++;
}
div=div>>1;
bits--;
count=count>>1;
//确定多项式形式的其他位的值(是1还是0)
int i;
long copyd;
for(i=bits-1;i>=0;i--){
copyd=labs(divisor);
div=div+(copyd<<i); //<<的优先级小于+,要加括号
//除数可以再加上copyd<<i,也即可以再累加上1<<i次
if(div<=ldividend){
count=count+(1<<i);
}
//不能加,尝试失败,除数的值还原到刚才未加时候的值
else{
copyd=labs(divisor);
div=div-(copyd<<i);
}
}
if(sign)
return count;
else
return -count;
}
遗留现象
1、上述代码是在以C++的形式下通过的,若以C的形式是过不了的。2、另外一个奇怪的现象是,上述代码在eclipse上运行,有些测试集的结果会和leetcode运行的不一样。
图1:leetcode编程环境下的测试截图
图2:eclipse C++的测试截图图1和图2使用的是相同的代码,见上文。然而测试结果不一样。在eclipse中debug会发现,即使是使用labs或者fabs先强制转换类型后来取绝对值(long temp=(long)dividend; long ldividend=labs(temp);),并使用long或double型来存储,-2147483648的绝对值依然是-2147483648,这也就是造成结果不对的原因。那为什么在leetcode的编程环境下运行就不会出现这种问题呢?我暂时还不清楚,知道的亲欢迎留言告诉我。
后续
在文章的开头,我说到直接单纯的一次一次的累加,可能会超时。那如果减少一半的累加次数,还会超时吗?于是我便写了下面的代码,与上文代码不同的地方是在确定指数级翻倍的最大翻倍数之后,采取的是简单的逐次循环累加统计次数。下面的代码提交,就有时候会Accept,有时候会提示Time limit Exceeded。所以,最好还是用上文代码的方法,一定不会超时。
class Solution
{
public:
int divide(int dividend, int divisor) {
if(divisor==0 || (dividend==INT_MIN && divisor==-1))
return INT_MAX;
bool sign=0;
if((dividend>0&&divisor>0)||(dividend<0&&divisor<0)){
sign=1;
}
long ldividend=labs(dividend);
long div=labs(divisor);
long count=1;
int bits=0; //bits为移的位数
if(div>ldividend)
return 0;
while(div<=ldividend){
count=count<<1;
div=div<<1;
bits++;
}
div=div>>1;
bits--;
count=count>>1;
long copyd=labs(divisor);;
while(div<=ldividend){
div=div+copyd;
count++;
}
if(div!=ldividend)
count--;
if(sign)
return count;
else
return -count;
}
};