欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 房产 > 家装 > 剑指Offer|day1 LCR 001. 两数相除

剑指Offer|day1 LCR 001. 两数相除

2025/4/19 3:08:09 来源:https://blog.csdn.net/Catherinemin/article/details/144432962  浏览:    关键词:剑指Offer|day1 LCR 001. 两数相除

LCR 001. 两数相除

给定两个整数 ab ,求它们的除法的商 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

法1:转为负数计算

分析:不能使用乘号和除号,只能使用减法。比如计算15/2(15是被除数,2是除数),就是15不停地减2,当减去7个2后余数是1,此时不能减去更多的2,因此15/2的商为7。

这个解法存在一个问题。当被除数很大除数很小,比如 ( 2 31 − 1 ) / 1 (2^{31}-1)/1 (2311)/1,减1操作需要执行 2 31 − 1 2^{31}-1 2311次,需要很长时间。

将上述解法稍作调整。当被除数大于除数的时候,继续比较判断被除数是否大于除数的2倍,如果是,则继续判断被除数是否大于除数的4倍、8倍等。如果被除数最多大于除数的 2 k 2^k 2k,那么将被除数减去除数的 2 k 2^k 2k倍,然后将剩余的被除数重复前面的步骤。每次将除数翻倍,因此优化后的时间复杂度为 O ( l o g n ) O(logn) O(logn)

看15/2讨论计算过程。15大于2,15也大于4,15大于8,但15小于16。于是先将15-8=7,由于减去的8是除数的4倍,所以这部分的商为4,结果加4;看7/2,7大于2,7大于4,7小于8,所以7-4=3,减去的4是除数的2倍,所以这部分的商为2,结果加2;看3/2,3大于2,3小于4,所以3-2=1,减去的2是除数的1倍,所以这部分的商为1,结果加1;最后的1比除数小,不能再减去除数了,所以结果为4+2+1=7。

上述讨论的是正整数,对于负数可都转为正数,算好之后再调整正负号。例如-15/2,先算15/2得到7,结果再加上符号。

将负数转换成正数存在一个问题,对于32位正数,最小整数 − 2 31 -2^{31} 231,最大的整数是 2 31 − 1 2^{31}-1 2311。因此最小整数转成正数 2 31 2^{31} 231会溢出。由于将任意正数转换为负数不会溢出,因此可以先将正数都转换成负数,再计算商,再调整商的正负号。

最后讨论可能的溢出。由于是整数的除法,并且除数不为0,因此商的绝对值一定小于或等于被除数的绝对值。因此,int型整数的除法只有一种情况会导致溢出,即 ( − 2 31 ) / ( − 1 ) (-2^{31})/(-1) (231)/(1)。这是因为最大的正数为 2 31 − 1 2^{31}-1 2311 2 31 2^{31} 231超出了正数的范围。

  • 时间复杂度O(log a)
  • 空间复杂度O(1)
var divide = function(a, b){// 解决溢出问题if(a === -(2**31) && b === -1) return 2**31-1;let negative = 2; // 计数变符号操作次数// 将a和b都变为负数if(a > 0){negative--;a = -a;}if(b > 0){negative--;b = -b;}let result = divideCore(a, b);return negative === 1 ? -result : result; // negative为1的话,就说明只有1个数是正数,所以结果要加负号}function divideCore(a, b) {let result = 0;// 当 a 小于等于 b 时,继续除法运算// (-15)/(-2)while (a <= b) {let value = b;let quotient = 1;// 通过倍增值和商来加速减法运算while (value >= -(2**30) && a <= value + value) {quotient += quotient;value += value;}result += quotient;a -= value;}return result;
}

来看divideCore函数,假设计算divideCore(-15, -2)

符合a <= b,value=-2,quotient=1,

value没有溢出&a也是符合条件的,quotient=2,value=-4;quotient=4,value=-8;不符合a <= value + value,跳出循环。

result=4,a=-15-(-8)=-7

计算(-7)/(-2)。符合a <= b,value=-2,quotient=1,

value没有溢出&a也是符合条件的,quotient=2,value=-4;不符合a <= value + value,跳出循环。

result=4+2=6,a=-7-(-4)=-3

计算(-3)/(-2)。符合a <= b,value=-2,quotient=1,

value没有溢出&a也是符合条件的,不符合a <= value + value,跳出循环。

result=6+1=7,a=-3-(-2)=-1;此时a=-1,b=-2,a>b跳出循环。

法2:转为正数计算

分析:和法1一样,这里是将所有数转为正数计算。其他基本一致

  • 时间复杂度O(log a)
  • 空间复杂度O(1)
var divide = function(a, b) {// 解决溢出问题if (a === -(2 ** 31) && b === -1) return 2 ** 31 - 1;// 判断符号let sign = (a > 0) ^ (b > 0);  // ^异或:同号0,异号1// 取正值a = a > 0 ? a : -a;b = b > 0 ? b : -b;let result = 0;// 进行除法计算while (a >= b) {let value = b;let k = 1;// 优化:倍增b,直到 value 加倍大于awhile (a >= (value + value)) {value += value;  // b 值加倍k += k;  // k 值也加倍}// 扣除当前的倍数,增加结果a -= value;result += k;}// 返回结果,根据符号判断return sign ? -result : result;
};

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词