剑指 offer 题目:
题号:LCR 001
给定两个整数 a
和 b
,求它们的除法的商 a/b
,要求不得使用乘号 '*'
、除号 '/'
以及求余符号 '%'
。
剑指 offer 解题思路:
思路一:暴力法
具体步骤如下:
-
初始化商为0。
-
当a大于等于b时,循环执行以下操作:
-
从a中减去b。
-
商加1。
-
-
循环结束后,商即为所求。
时间复杂度:O(N)
空间复杂度:O(1)
思路一:类二分法(以 C++ 为例)
一、初始设置
算法首先处理一些特殊情况,比如被除数为整数的最小值(INT_MIN
)或除数为INT_MIN
,以及被除数为0的情况。这些特殊情况需要单独处理,因为它们在常规的除法运算中可能会导致溢出或除零错误。
二、正负转换
为了简化问题,算法将所有的正数都转换为负数来处理。这样做的好处是只需要考虑一种情况(即负数相除),从而避免了正负数相除时的复杂性。布尔变量rev
用于记录最终结果是否需要取反。
三、候选值生成
算法使用一个数组(在这里是vector<int>
类型的candidates
)来存储候选的除数。初始时,数组中只有一个元素,即转换后的除数b
。然后,通过一个循环将候选值不断加倍(即candidates.back() + candidates.back()
),直到最后一个候选值大于或等于被除数a
减去当前候选值的差值。这个过程实际上是在生成一个以b
为首项,公比为2的等比数列,但受限于a
的大小以防止溢出。
四、结果累加
从候选值数组的最后一个元素开始,算法检查当前候选值是否大于或等于被除数a
。如果是,则将对应的二进制位(通过1 << i
计算得出,其中i
是当前候选值在数组中的索引)累加到结果ans
中,并从被除数a
中减去当前候选值。这个累加过程类似于二进制数的运算,因为候选值数组是通过不断加倍生成的,所以每个候选值都对应了一个二进制位。
五、结果取反
最后,根据布尔变量rev
的值,如果最终结果为负数,则将其取反(即变为正数),否则保持不变。
与标准二分搜索的区别
需要注意的是,这个算法虽然类似于二分查找,但并非标准的二分搜索。标准的二分查找是在有序数组中查找特定元素的过程,通过不断折半来逼近目标值。而这里的类二分查找算法是通过不断加倍除数来逼近被除数,从而计算出商。
时间复杂度:O(log n)
空间复杂度:O(log n)
C++
// C++
class Solution {
public:
int divide(int a, int b) {
// 被除数为最小值
if (a == INT_MIN) {
if (b == 1) {
return INT_MIN;
}
if (b == -1) {
return INT_MAX;
}
}
// 除数为最小值
if (b == INT_MIN) {
return a == INT_MIN ? 1 : 0;
}
// 考虑被除数为 0 的情况
if (a == 0) {
return 0;
}
// 将所有的正数取相反数,这样就只需要考虑一种情况
bool rev = false;
if (a > 0) {
a = -a;
rev = !rev;
}
if (b > 0) {
b = -b;
rev = !rev;
}
vector<int> candidates = {b};
// 注意溢出
while (candidates.back() >= a - candidates.back()) {
candidates.push_back(candidates.back() + candidates.back());
}
int ans = 0;
for (int i = candidates.size() - 1; i >= 0; --i) {
if (candidates[i] >= a) {
ans += (1 << i);
a -= candidates[i];
}
}
return rev ? -ans : ans;
}
};
go
// go
func divide(a, b int) int {
if a == math.MinInt32 {
// 被除数为最小值
if b == 1 {
return math.MinInt32
}
if b == -1 {
return math.MaxInt32
}
}
if b == math.MinInt32 {
// 除数为最小值
if a == math.MinInt32 {
return 1
}
return 0
}
if a == 0 {
// 被除数为 0
return 0
}
// 将所有的正数取相反数,这样就只需要考虑一种情况
rev := false
if a > 0 {
a = -a
rev = !rev
}
if b > 0 {
b = -b
rev = !rev
}
candidates := []int{b}
for y := b; y >= a-y; { // 注意溢出
y += y
candidates = append(candidates, y)
}
ans := 0
for i := len(candidates) - 1; i >= 0; i-- {
if candidates[i] >= a {
ans |= 1 << i
a -= candidates[i]
}
}
if rev {
return -ans
}
return ans
}