Given two integers dividend
and divisor
, divide two integers without using multiplication, division, and mod operator.
The integer division should truncate toward zero, which means losing its fractional part. For example, 8.345
would be truncated to 8
, and -2.7335
would be truncated to -2
.
Return the quotient after dividing dividend
by divisor
.
Note:
Assume we are dealing with an environment that could only store integers within the 32-bit signed integer range: [ − 2 31 , 2 31 − 1 ] [−2^{31}, 2^{31} − 1] [−231,231−1]. For this problem, if the quotient is strictly greater than 2 31 − 1 2^{31} - 1 231−1, then return 2 31 − 1 2^{31} - 1 231−1, and if the quotient is strictly less than − 2 31 -2^{31} −231, then return − 2 31 -2^{31} −231.
Example 1:
Input: dividend = 10, divisor = 3
Output: 3
Explanation: 10/3 = 3.33333.. which is truncated to 3.
Example 2:
Input: dividend = 7, divisor = -3
Output: -2
Explanation: 7/-3 = -2.33333.. which is truncated to -2.
Constraints:
-231 <= dividend, divisor <= 231 - 1
divisor != 0
AC:
/*
* @lc app=leetcode.cn id=29 lang=cpp
*
* [29] 两数相除
*/
// @lc code=start
class Solution {
public:
int divide(int dividend, int divisor) {
// 特殊情况处理
if(dividend == INT_MIN) {
if(divisor == 1) {
return INT_MIN;
}
if(divisor == -1) {
return INT_MAX;
}
}
if(divisor == INT_MIN) {
return dividend == INT_MIN ? 1 : 0;
}
if(dividend == 0) {
return 0;
}
bool rev = false;
// 将被除数和除数转换为负数,方便处理
if(dividend > 0) {
dividend = -dividend;
rev = !rev;
}
if(divisor > 0) {
divisor = -divisor;
rev = !rev;
}
// 用于计算两个负数相乘的结果是否超出了 int 类型的范围。
auto quickAdd = [](int y, int z, int x) {
int result = 0, add = y;
while(z) {
//判断 z 是否为奇数
if(z & 1) {
if(result < x - add)
{
return false;
}
result += add;
}
//用于计算两个负数相乘的结果是否超出了 int 类型的范围。
//如果超出了范围,就返回 false,否则将结果加到 result 中。
if(z != 1) {
if(add < x - add)
{
return false;
}
add += add;
}
z >>= 1;
}
return true;
};
int left = 1, right = INT_MAX, ans = 0;
// 二分查找
while(left <= right) {
int mid = left +((right - left) >> 1);
bool check = quickAdd(divisor, mid, dividend);
if(check) {
ans = mid;
if(mid == INT_MAX) {
break;
}
left = mid + 1;
}
else {
right = mid - 1;
}
}
return rev ? -ans : ans;
}
};
// @lc code=end
Thought:
(code不出,参考了官方题解)
首先处理特殊情况,如果被除数为 INT_MIN
,那么当除数为 1
时,结果为 INT_MIN
,当除数为 -1 时,结果为 INT_MAX
。如果除数为 INT_MIN
,那么当被除数为 INT_MIN
时,结果为 1
,否则结果为 0
。如果被除数为 0
,那么结果为 0
。
将被除数和除数转换为负数,方便处理。同时记录一个变量 rev
,表示最终结果是否为负数。
定义一个函数 quickAdd
,用于计算两个负数相乘的结果是否超出了 int
类型的范围。如果超出了范围,就返回 false
,否则将结果加到 result
中。
二分查找,查找范围为 [1, INT_MAX]
,每次取中间值 mid
,然后调用 quickAdd
函数判断 divisor * mid
是否小于等于 dividend
。如果小于等于,说明 mid
可以作为结果,更新 ans
的值,并将查找范围缩小到 [mid + 1, right]
,否则将查找范围缩小到 [left, mid - 1]
。
最后根据 rev
的值返回结果,如果为 true
,说明结果为负数,需要将 ans
取反。
Supplements:
快速乘法
和快速幂
是两个不同的算法。
快速乘法用于高精度乘法
,可以在 O ( n log n ) O(n \log n) O(nlogn) 的时间复杂度内完成两个 n n n 位数的乘法。其基本思想是将整数按位拆分成较小的部分,然后在乘法运算中通过组合这些部分来得到结果。例如,对于两个 n n n 位数 a a a 和 b b b,可以将它们分别拆分成 n / 2 n/2 n/2 位的高半部分 a 1 a_1 a1 和 b 1 b_1 b1,以及 n / 2 n/2 n/2 位的低半部分 a 0 a_0 a0 和 b 0 b_0 b0,然后使用如下公式计算乘积:
a b = a 1 b 1 ⋅ 1 0 2 ( n / 2 ) + ( a 1 b 0 + a 0 b 1 ) ⋅ 1 0 n / 2 + a 0 b 0 ab = a_1b_1 \cdot 10^{2(n/2)} + (a_1b_0 + a_0b_1) \cdot 10^{n/2} + a_0b_0 ab=a1b1⋅102(n/2)+(a1b0+a0b1)⋅10n/2+a0b0
这样,原来的乘法问题就被转化成了四个规模为 n / 2 n/2 n/2 的乘法问题和一些小规模的加法和移位问题。
相比传统的暴力乘法算法,快速乘法可以极大地加速乘法运算,尤其是在需要处理大数乘法时,效果更为明显。
快速幂用于求幂运算,即计算一个数的 n n n 次幂。它可以在 O ( log n ) O(\log n) O(logn) 的时间复杂度内完成这个计算。其基本思想是将幂运算拆分成多个指数为 2 2 2 的幂次方相乘的形式,例如:
a 5 = a ( 2 2 + 2 0 ) = a 2 2 ⋅ a 2 0 = a 4 ⋅ a a^5 = a^{(2^2 + 2^0)} = a^{2^2} \cdot a^{2^0} = a^4 \cdot a a5=a(22+20)=a22⋅a20=a4⋅a
这样,原问题就被转化成了两个规模为 n / 2 n/2 n/2 的子问题,每个子问题都只需要计算一次。通过递归调用,可以在 O ( log n ) O(\log n) O(logn) 的时间复杂度内完成整个运算过程。
相比传统的暴力幂算法,快速幂可以大幅度降低计算次数
,从而提高幂运算的效率。快速乘法和快速幂是两个不同的算法。
快速乘法用于高精度乘法,可以在 O ( n log n ) O(n \log n) O(nlogn) 的时间复杂度内完成两个 n n n 位数的乘法。其基本思想是将整数按位拆分成较小的部分,然后在乘法运算中通过组合这些部分来得到结果。例如,对于两个 n n n 位数 a a a 和 b b b,可以将它们分别拆分成 n / 2 n/2 n/2 位的高半部分 a 1 a_1 a1 和 b 1 b_1 b1,以及 n / 2 n/2 n/2 位的低半部分 a 0 a_0 a0 和 b 0 b_0 b0,然后使用如下公式计算乘积:
a b = a 1 b 1 ⋅ 1 0 2 ( n / 2 ) + ( a 1 b 0 + a 0 b 1 ) ⋅ 1 0 n / 2 + a 0 b 0 ab = a_1b_1 \cdot 10^{2(n/2)} + (a_1b_0 + a_0b_1) \cdot 10^{n/2} + a_0b_0 ab=a1b1⋅102(n/2)+(a1b0+a0b1)⋅10n/2+a0b0
这样,原来的乘法问题就被转化成了四个规模为 n / 2 n/2 n/2 的乘法问题和一些小规模的加法和移位问题。
相比传统的暴力乘法算法,快速乘法可以极大地加速乘法运算,尤其是在需要处理大数乘法时,效果更为明显。
快速幂用于求幂运算,即计算一个数的 n n n 次幂。它可以在 O ( log n ) O(\log n) O(logn) 的时间复杂度内完成这个计算。其基本思想是将幂运算拆分成多个指数为 2 2 2 的幂次方相乘的形式,例如:
a 5 = a ( 2 2 + 2 0 ) = a 2 2 ⋅ a 2 0 = a 4 ⋅ a a^5 = a^{(2^2 + 2^0)} = a^{2^2} \cdot a^{2^0} = a^4 \cdot a a5=a(22+20)=a22⋅a20=a4⋅a
这样,原问题就被转化成了两个规模为 n / 2 n/2 n/2 的子问题,每个子问题都只需要计算一次。通过递归调用,可以在 O ( log n ) O(\log n) O(logn) 的时间复杂度内完成整个运算过程。
相比传统的暴力幂算法,快速幂可以大幅度降低计算次数,从而提高幂运算的效率。
添加一个递归实现的做法:
/*
* @lc app=leetcode.cn id=29 lang=cpp
*
* [29] 两数相除
*/
// @lc code=start
class Solution {
public:
int divide(int dividend, int divisor) {
if(dividend == INT_MIN && divisor == -1)
return INT_MAX;
if(dividend == INT_MIN && divisor == 1)
return INT_MIN;
if(dividend > 0)
return -divide(-dividend, divisor); // 如果被除数为正数,则将被除数和除数都取相反数,这样就可以转化为被除数为负数的情况
if(divisor > 0)
return -divide(dividend, -divisor); // 如果除数为正数,则将被除数和除数都取相反数,这样就可以转化为除数为负数的情况
if(dividend > divisor)
return 0; // 如果被除数小于除数,则返回0
int res = 1, tmp = divisor;
while((dividend - divisor) <= divisor)
{
res += res;
divisor += divisor;
}
return res + divide(dividend - divisor, tmp); // 递归计算被除数减去除数的结果和除数的商
}
};
// @lc code=end