【力扣Leetcode题解系列之0029—两数相除】

29.两数相除

题目简要介绍

本题要求在不使用乘法、除法和取余运算的前提下,实现两个整数的除法,并将结果向零截断。同时,要考虑结果在32位有符号整数范围内的限制,如果商超出范围,需返回相应的边界值。这道题主要考查对基本运算原理的理解以及如何通过其他运算来模拟除法运算。

各语言实现

Python实现
class Solution:
    def divide(self, dividend: int, divisor: int) -> int:
        INT_MIN, INT_MAX = -2**31, 2**31 - 1
        # 处理特殊情况
        if dividend == INT_MIN and divisor == -1:
            return INT_MAX
        if dividend == INT_MIN and divisor == 1:
            return INT_MIN
        # 判断结果正负
        sign = -1 if (dividend < 0) ^ (divisor < 0) else 1
        dividend, divisor = abs(dividend), abs(divisor)
        result = 0
        while dividend >= divisor:
            temp, multiple = divisor, 1
            while dividend >= temp << 1:
                temp <<= 1
                multiple <<= 1
            dividend -= temp
            result += multiple
        return sign * result
Java实现
class Solution {
    public int divide(int dividend, int divisor) {
        if (dividend == Integer.MIN_VALUE && divisor == -1) {
            return Integer.MAX_VALUE;
        }
        if (dividend == Integer.MIN_VALUE && divisor == 1) {
            return Integer.MIN_VALUE;
        }
        boolean isNegative = (dividend < 0) ^ (divisor < 0);
        long dividendAbs = Math.abs((long) dividend);
        long divisorAbs = Math.abs((long) divisor);
        int result = 0;
        while (dividendAbs >= divisorAbs) {
            long temp = divisorAbs;
            int multiple = 1;
            while (dividendAbs >= (temp << 1)) {
                temp <<= 1;
                multiple <<= 1;
            }
            dividendAbs -= temp;
            result += multiple;
        }
        return isNegative? -result : result;
    }
}
C实现
#include <stdio.h>
#include <limits.h>

int divide(int dividend, int divisor) {
    if (dividend == INT_MIN && divisor == -1) {
        return INT_MAX;
    }
    if (dividend == INT_MIN && divisor == 1) {
        return INT_MIN;
    }
    int isNegative = (dividend < 0) ^ (divisor < 0);
    long dividendAbs = labs(dividend);
    long divisorAbs = labs(divisor);
    int result = 0;
    while (dividendAbs >= divisorAbs) {
        long temp = divisorAbs;
        int multiple = 1;
        while (dividendAbs >= (temp << 1)) {
            temp <<= 1;
            multiple <<= 1;
        }
        dividendAbs -= temp;
        result += multiple;
    }
    return isNegative? -result : result;
}
C++实现
#include <iostream>
#include <limits>

class Solution {
public:
    int divide(int dividend, int divisor) {
        if (dividend == std::numeric_limits<int>::min() && divisor == -1) {
            return std::numeric_limits<int>::max();
        }
        if (dividend == std::numeric_limits<int>::min() && divisor == 1) {
            return std::numeric_limits<int>::min();
        }
        bool isNegative = (dividend < 0) ^ (divisor < 0);
        long long dividendAbs = std::abs((long long)dividend);
        long long divisorAbs = std::abs((long long)divisor);
        int result = 0;
        while (dividendAbs >= divisorAbs) {
            long long temp = divisorAbs;
            int multiple = 1;
            while (dividendAbs >= (temp << 1)) {
                temp <<= 1;
                multiple <<= 1;
            }
            dividendAbs -= temp;
            result += multiple;
        }
        return isNegative? -result : result;
    }
};
Go实现
func divide(dividend int, divisor int) int {
    const (
        MinInt32 = -1 << 31
        MaxInt32 =  1<<31 - 1
    )
    if dividend == MinInt32 && divisor == -1 {
        return MaxInt32
    }
    if dividend == MinInt32 && divisor == 1 {
        return MinInt32
    }
    sign := 1
    if (dividend < 0) != (divisor < 0) {
        sign = -1
    }
    dividendAbs := abs(dividend)
    divisorAbs := abs(divisor)
    result := 0
    for dividendAbs >= divisorAbs {
        temp := divisorAbs
        multiple := 1
        for dividendAbs >= (temp << 1) {
            temp <<= 1
            multiple <<= 1
        }
        dividendAbs -= temp
        result += multiple
    }
    if sign == -1 {
        return -result
    }
    return result
}

func abs(a int) int {
    if a < 0 {
        return -a
    }
    return a
}

算法复杂性分析

  1. 时间复杂度:时间复杂度取决于内层循环的执行次数。在内层循环中,每次通过将 temp 左移(相当于乘以2)来找到最大的 multiple,使得 dividendAbs 仍大于等于 temp。由于 temp 每次翻倍,所以内层循环的执行次数与 dividendAbsdivisorAbs 的大小关系有关,大致为 O(log⁡n)O(\log n)O(logn),其中 nnndividendAbsdivisorAbs 的比值。外层循环每次减去找到的 temp,总的时间复杂度也是 O(log⁡n)O(\log n)O(logn)
  2. 空间复杂度:所有实现的空间复杂度均为 O(1)O(1)O(1),因为只使用了常数级别的额外空间,不随输入的 dividenddivisor 的大小而改变。

常用解法

  1. 倍增法
    • 首先确定结果的正负号,通过异或运算 (dividend < 0) ^ (divisor < 0) 判断。
    • 将被除数和除数都取绝对值,方便后续计算。
    • 利用移位运算(左移相当于乘以2)来模拟除法。从除数开始,每次将除数翻倍,同时记录翻倍的倍数,直到翻倍后的除数大于被除数。然后从被除数中减去这个翻倍后的除数,并将对应的倍数累加到结果中。重复这个过程,直到被除数小于除数。
  2. 处理边界情况:特别处理 dividendINT_MINdivisor 为 -1 或 1 的情况,因为这两种情况会导致结果溢出。

实现的关键点和难度

  1. 关键点
    • 边界条件处理:正确处理 dividendINT_MINdivisor 为 -1 或 1 的特殊情况,这是避免结果溢出的关键。
    • 移位运算模拟除法:理解并正确使用移位运算来模拟乘法和除法,通过不断翻倍除数找到合适的倍数,从而实现除法运算。
    • 符号判断与处理:准确判断结果的正负号,并在最后返回结果时应用这个符号。
  2. 难度
    • 理解移位运算原理:需要深入理解移位运算的原理和效果,以及如何利用它来模拟乘法和除法。这需要对计算机底层的二进制运算有一定的了解,对于不熟悉二进制运算的开发者来说有一定难度。
    • 处理溢出情况:除了题目明确指出的 dividendINT_MINdivisor 为 -1 或 1 的情况,在整个计算过程中,由于使用了 longlong long 类型来避免中间结果溢出,需要确保类型转换和运算的正确性,防止其他潜在的溢出情况。

扩展及难度加深题目

  1. 扩展题目
    • 实现带余数的除法:在不使用乘法、除法和取余运算的前提下,不仅返回商,还返回余数。这需要在现有算法基础上,记录每次减去 temp 后剩余的部分作为余数。
    • 支持浮点数除法:扩展算法以支持浮点数的除法运算,同样不使用乘法、除法和取余运算。这需要处理小数部分的计算,可能涉及到对精度的控制和更多的逻辑处理。
  2. 难度加深题目
    • 处理高精度除法:假设输入的被除数和除数可以是任意大的整数(超过32位有符号整数范围),实现高精度除法,不使用乘法、除法和取余运算。这需要使用数组或链表来存储大整数,并设计相应的算法来模拟除法过程。
    • 优化算法性能:在不改变基本思路的前提下,进一步优化算法的时间复杂度和空间复杂度,例如通过更高效的查找合适倍数的方法,或者减少中间变量的使用。

应用场合

  1. 底层系统开发:在一些底层系统或嵌入式系统中,可能由于硬件限制或特定的编程要求,无法直接使用乘法、除法和取余指令。此时,这种模拟除法的算法可以用于实现基本的数学运算。
  2. 密码学相关:在某些密码算法中,为了保证安全性和特定的运算规则,可能需要通过基本的位运算来实现除法功能,以避免使用常规的算术运算指令,防止被破解。
  3. 算法学习与理解:作为一种对基本运算原理深入理解的练习,有助于开发者更好地掌握计算机底层运算机制,提升算法设计和编程能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值