一.题目描述以及来源
给定两个整数 a
和 b
,求它们的除法的商 a/b
,要求不得使用乘号 '*'
、除号 '/'
以及求余符号 '%'
。
注意:
整数除法的结果应当截去(truncate)其小数部分,例如:truncate(8.345) = 8 以及 truncate(-2.7335) = -2
假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−231, 231−1]。本题中,如果除法结果溢出,则返回 231 − 1
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/xoh6Oh
例子:
输入:a = 15, b = 2
输出:7
解释:15/2 = truncate(7.5) = 7
输入:a = 7, b = -3
输出:-2
解释:7/-3 = truncate(-2.33333..) = -2
输入:a = 0, b = 1
输出:0
输入:a = 1, b = 1
输出:1
二.解题思路
a=15 | b=2 | k=0 |
15>=b(a>=b) | a=13 | k=1 |
15-2>=b | k=2 | |
13-2>=b | 3 | |
11-2>=b | 4 | |
9-2>=b | 5 | |
7-2>=b | 6 | |
5-2>=b | 7 | |
3-2<b | end |
因为不能用除法,所以只能使用减法去实现除法。
实现的代码如下:
public static void Divide(int a, int b)
{
//int sign = 1;
//if((a>0 && b<0)||(a<0 && b > 0))
//{
// sign= -1;
//}
int sign = (a > 0) ^ (b > 0) ? -1 : 1;
a=Math.Abs(a); b=Math.Abs(b);
int res = 0;
while(a>=b)
{
a=a-b;
res++;
}
if(sign==-1)
{
res = -res;
}
System.Console.WriteLine(res);
}
此外还需要考虑,除法的符号,以及边界问题
//时间复杂度是O(n),
public static int DivideRange(int a, int b)
{
//32位最大值: 2^31-1=2147483647
//32为最小值:-2^31=—2147483648
//—2147483648/(-1)=2147483648>—2147483647 越界
if ((a == (-2147483648)) && (b == (-1))){
return 2147483647;
}
int sign = (a > 0) ^ (b > 0) ? -1 : 1;
//Math.Abs(—2147483648)=—2147483648 所以我们将正数转成负数去计算
if (a > 0)
{
a = -a;
}
if (b > 0)
{
b = -b;
}
int res = 0;
//-7 /-2 =3 ,都是负数除法 转成 数学,循环是<= 例如 -7<=-2 ,继续循环
while (a <= b)
{
a = a - b;
res++;
}
if (sign == -1)
{
res = -res;
}
return res;
}
上面为解体的完整代码,需要注意的有:
1.范围时,注意范围的边界(最小值/-1 会越界,最小值取正数 会保持原值)
2.将所有数转成负数去进行计算,因为最大值可以取相反数,最小值相反数越界
3.原本数字都变成正数的时候,判断是a>=b,作为循环的条件。转成负数计算的时候,循环的条件是a<=b
4.在c#中Integer.MIN_VALUE为
int max = int.MaxValue;
int min = int.MinValue;
三. 解题思路(2)
此思路看不懂的话可以去看看leetcode讲解视频:简单易懂Java/C++ /Python/js/go - 整数除法(剑指) - 整数除法 - 力扣(LeetCode)
public static int DivideVersion2(int a, int b)
{
//32位最大值: 2^31-1=2147483647
//32为最小值:-2^31=—2147483648
//—2147483648/(-1)=2147483648>—2147483647 越界
if ((a == (-2147483648)) && (b == (-1)))
{
return 2147483647;
}
int sign = (a > 0) ^ (b > 0) ? -1 : 1;
//Math.Abs(—2147483648)=—2147483648 所以我们将正数转成负数去计算
if (a > 0)
{
a = -a;
}
if (b > 0)
{
b = -b;
}
int res = 0;
// 22/7
while (a <= b)
{
int value = b;
int k = 1;
while (a < value + value)
{
k = k + k;
value = value + value;
}
res += k;
a = a - value;
}
if (sign == -1)
{
res = -res;
}
System.Console.WriteLine(res);
return res;
}
此方法会超时,而且会有越域的情况
四,解题思路(3)位运算
在三的解题思路之上,我们知道我们可以实行b*2去减去a一半的情况。将b*2转换成b<<2,b往左移一位。
public class Solution {
public int Divide(int a, int b) {
if ((a == int.MinValue) && (b == (-1)))
{
return int.MaxValue;
}
int res = 0;
if (b == int.MinValue) {
return a == b? 1 : 0;
}
// 被除数先减去一个除数
if (a == int.MinValue) {
a -= -Math.Abs(b);
res += 1;
}
int sign = (a > 0) ^ (b > 0) ? -1 : 1;
a = Math.Abs(a); b = Math.Abs(b);
for(int i = 31; i >= 0; i--)
{
if ((a >> i) - b >= 0)
{
if (res > int.MaxValue - (1 << i)) {
return int.MinValue;
}
a=a-(b<<i);
res = res + (1<<i);
}
}
if (sign == -1)
{
res = -res;
}
return res;
}
}
上面for循环内实现了整个逻辑,需要注意的是i=0是表示-b本身,所以需要i>=0.
以下是处理边界值的代码,测试之前没发现。
if (b == int.MinValue) {
return a == b? 1 : 0;
}
// 被除数先减去一个除数
if (a == int.MinValue) {
a -= -Math.Abs(b);
res += 1;
}