leetcode 29.两数相除

leetcode 29.两数相除

题目描述

给定两个整数,被除数 dividend 和除数 divisor。将两数相除,要求不使用乘法、除法和 mod 运算符。

返回被除数 dividend 除以除数 divisor 得到的商。

示例 1:

输入: dividend = 10, divisor = 3
输出: 3

示例 2:

输入: dividend = 7, divisor = -3
输出: -2

说明:

  • 被除数和除数均为 32 位有符号整数。
  • 除数不为 0。
  • 假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−231, 231 − 1]。本题中,如果除法结果溢出,则返回 231 − 1。

解题思路

两数两除,不能使用乘法、除法和mod运算符,那么一个很简单的思路就出来了,就是不断的用被除数不断的减去除数,直到被除数小于除数的时候,结束迭代,但是如果这样做的话,势必会超时,举个例子 dividend=INT_MIN, divisor=1,如果这样迭代的话,就要花费很长的时间。
在计算机里,位运算的执行效率是非常高的,一个数字左移一位相当于除以2,右移一位相当于乘以2,那么就可以采用这样的方法来减少迭代次数。意思是,每一次相减都让除数的值翻倍,当然累加次数也要翻倍,直到dividend小于divisor的时候停止。举几个简单的例子

例子一: dividend = 10, divisor = 1

  • 采用保留求解,就是10不停的减去1,那么需要循环十次。

  • 采用位移的方法 ,是怎么操作的呢,看下面的表格,

    1. 首先建立一个临时的变量temp=divisor,让dividend去减temp,count记一,这时候发现dividend大于等于temp,那么dividor翻倍,count也翻倍,然后发现dividend仍然大于等于temp,temp继续翻倍,那么count也应该在原来的基础上翻倍(记录被减了多少次);
    2. 再继续判断temp继续翻倍的话,dividend就小于temp了,这时候dividend=2,让此时的dividend去和divisor比较,发现dividend大于等于divisor,那么就继续把temp=divisor,循环上面的操作

    上面写的步骤只是为了好理解,其实就是 dividend = 8 + 2 + 余数 = 23*1 + 21*1 + 余数

    dividend temp count
    10 >= 2 1 1
    10 >= 4 2 2
    10 >= 8 4 4
    10 < 16,此时10减去8,并与divisor比较 8 8 此时res+8
    2 >= 2 1 1
    2 < 4 此时 2减去2,并与divisor比较 2 2 此时res+2
    合计 res 10

例子二:dividend = 123, divisor = 3;

123 = 25*3 + 23*3 + 3

dividend temp count
123 >= 6 3 1
123 >= 12 6 2
123 >= 24 12 4
123 >= 48 24 8
123 >= 96 48 16
123 < 192 此时 123减去96,并与divisor比较 96 32 此时res+32
27 >= 6 3 1
27 >= 12 6 2
27 >= 24 12 4
27 <= 48 此时27减去24,并与divisor比较 24 8 此时res+8
3 < 6 此时 3减去3,并与divisor比较 3 1 此时res+1
合计 res 41

通过上面的例子就可以知道,通过位移的方法可以大大加上迭代的次数,提高运算效率,具体代码如下

class Solution {
public:
    int divide(int dividend, int divisor) {
        if(divisor == 0 || dividend == 0){      // 被除数为零或者除数为零直接返回零
            return 0;
        }
        if(divisor == 1){     // 如果除数是1,就不需要额外的操作,直接返回被除数本身
            return dividend;
        }
        // 当被除数为INT_MIN, 除数是-1时需要特别注意,会出现溢出的情况,因为int范围是-2147483648~2147483647,相除的结果就是2147483648,当然是溢出;所以这里做一个特殊处理
        if(divisor == -1 && dividend == INT_MIN){  
            return INT_MAX;
        }
        bool sign = (dividend > 0)^(divisor > 0);  // 通过异或, 判断是不是异号
        long a = dividend, b = divisor;         // 这里把两个数转成long类型,并取绝对值,取绝对值是为了好运算,如果是不采用绝对值,就要分四种情况(--,++,+-,-+)来讨论反而更复杂
        a = abs(a);
        b = abs(b);
        int res = 0;
        while(a >= b){  // 如果a<b,那么循环终止
            int count = 1;
            long temp = b;
            while(a >= (temp<<1)){  // 不断的采用位移操作,尽可能减去最大的除数的倍数
                temp = temp << 1;
                count = count << 1;
            }
            a -= temp;
            res += count;
        } 
        // 通过sign判断是否异号,并在结果res上添加对应符号
        return sign ? -res : res;
    }
};

欢迎大家关注我的个人公众号,同样的也是和该博客账号一样,专注分享技术问题,我们一起学习进步
在这里插入图片描述

发布了135 篇原创文章 · 获赞 164 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/EngineerHe/article/details/103920952