题目
来源:leetcode =>原题链接
给定两个整数 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
提示:
- -231 <= a, b <= 231 - 1
- b != 0
思路
整数除法可以当作减法,对于整数a/b的计算,最简单的方法就是定义一个初始变量 res=0 为商,a每减去一个b,res+1,直到 |a|< |b| 为止。
考虑几种特殊情况
- b != 0
- a=-231-1, b=-1 时,返回结果正数的最大值 231,防止溢出
- 符号问题,a b 同号结果为正,a b 异号结果为负
- 范围问题,正数的范围比负数的范围小,可以把a b全部转成负数计算,标记a b异号情况,最后对结果取负即可
加速计算过程
当除数太小(b=1, b=2),被除数a较大时,减法次数最好越少越好
- b=1时,res = a
- b=2时,res=a>>1
……(除数不必穷尽列举) - 若a能除以2b,则商res也乘2:即res=1,|a| > |b+b|时,a=a-b-b,b=b+b,res=res+res,(b > -230)。这样的商是指数级别的递增
解法
/*
* 实现32位整数除法运算,不使用/, *, %
* 只保留除数的整数部分
* 考虑溢出情况,-2^31-1 ~ 2^31
* */
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
int divide_neg(int a, int b)
{
int tmp = a;
int result = 0;
while (tmp<=b) {
int cnt = 1;
int flag = b;
/* 加速除的过程,妙 */
while (flag>-1079741824&&tmp<=(flag+flag)) {
flag+=flag;
cnt+=cnt;
printf("%d flag: %d, cnt: %d\n", __LINE__, flag, cnt);
}
tmp -= flag;
result += cnt;
printf("%d tmp: %d; result: %d\n", __LINE__, tmp, result);
}
return result;
}
int divide(int a, int b){
int max_value = INT32_MAX;
int min_value = INT32_MIN;
int res = 0;
int sign = -1;
if (b == 0) {
printf("divisor value can't be zero\n");
exit(-1);
}
if (a == 0) return 0;
if (a == min_value && b == -1) return max_value;
if (b == 1) return a;
if (b == 2) return a >> 1; //除数2太小,直接按二进制移位快
if (b == min_value) return a == min_value? 1:0;//可判可不判
/* 异号结果为负,标记该情况 */
if ((a>0 && b<0)||(a<0 && b>0)) sign = 1;
/* 负数范围比正数大1,全按负负除法计算 */
if (a>0) a = -a;
if (b>0) b = -b;
res = divide_neg(a, b);
int result = sign == 1?-res:res;
return result;
}