Offer 驾到,掘友接招!我正在参与 2022 春招打卡活动,点击查看活动详情。
一、题目描述:
- 两数相除 —— 难度中等
给定两个整数,被除数 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。
二、题目和思路分析:
要求不适用乘法,除法和mod运算符(取余),也好像不是很难的样子,毕竟还可以用+-嘛!
那么怎么想想呢?用加减法来实现除法,好像也不是特别复杂,毕竟乘除法本身就是加减法的进阶而已。
稍微一想,就会想到:
被除数减去除数,得到的值大于除数的话,再减去除数,直到小于除数,计减去的次数。
这样一个个减,可能有些慢,那么有没有快一些的方法呢?或者说在减的基础上如何优化呢?
可以两个两个的减,如果发现超了,反过来再加一个对比一下。
不过这样想想也没什么技术难度,翻开评论区看看各位大神的想法,才知道大佬们都是用位运算搞定的。
可是使用位运算,本质上不也是使用了乘法嘛!并且如果我不看评论区,永远想不到还能这么做,哼,我想不到的方法算什么好方法?
评论区是靠不住的,还是要靠自己!
一个个减或者两个两个减,还是会超时,那么如果每次让除数翻一倍呢?
这样想起来运算量就会少很多,当前的除数如果大于被除数,直接返回0。否则的话,就进行循环判断,只要当前除数加自身还是小于被除数,那么除数就再次翻倍,同时记录除数翻倍次数作为加的记录。
逻辑没得问题,代码调试起来!
三、代码:
代码实现如下:
/**
* @param {number} dividend
* @param {number} divisor
* @return {number}
*/
var divide = function(dividend, divisor) {
let p = true // Positive默认商为正数
if((dividend < 0 && divisor > 0) || (dividend > 0 && divisor < 0)){ // 如果除数与被除数正负相反,结果为负数
p = false
}
dividend = Math.abs(dividend)
divisor = Math.abs(divisor)
let res = fun(dividend, divisor)
p || (res = -res)
return (res > 2147483647) ? 2147483647 : res
function fun (a, b){
if (a < b) return 0 // 当 被除数<除数 时返回0
let cnt = 1 // 记录目前相加的是几个除数
let temp = b // 除数赋值给temp
while ((temp + temp) < a) { // 如果两个除数相加还是小于被除数
cnt = cnt + cnt // 记录数也相应翻倍
temp = temp + temp // 除数翻倍
}
// 此时temp为b的cnt倍,并且temp小于a,所以继续循环遍历a - temp, b即可。
return cnt + fun(a - temp, b) // 继续以上一个余下的值,和除数代入方法循环
}
};
四、总结:
这道题还是很有意思的,位运算解题在我看来明显又复杂又违反规则,我写的这种方法才是真正的解题方法!
加油吧!