leetcode29两数相除

题目描述

给定两个整数,被除数 dividend 和除数 divisor。将两数相除,要求不使用乘法、除法和 mod 运算符。
返回被除数 dividend 除以除数 divisor 得到的商。
整数除法的结果应当截去(truncate)其小数部分,例如:truncate(8.345) = 8 以及 truncate(-2.7335) = -2
示例 1:
输入: dividend = 10, divisor = 3
输出: 3
解释: 10/3 = truncate(3.33333..) = truncate(3) = 3
示例 2:
输入: dividend = 7, divisor = -3
输出: -2
解释: 7/-3 = truncate(-2.33333..) = -2

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

题目理解

  • 可以使用long型
  • 不可以使用long型
溢出的情况
     * 1. 两数相加,如负数 + 负数, 正数 + 正数
     * 2.两数相除,如-2^31 / -1 答案为 2^31次方,产生溢出,
     * 如何判断两数为异号?
     * 为1说明异号,为零说明同号
     * int symbol = (dividend ^ divisor) >>> 31;
     * 如何解决溢出?将int型转换为long型

暴力超时

x / y 说明 y * n >= x ,将y累加,如果大于x,得到结果

public static int divide(int dividend, int divisor) {
        int symbol = (((dividend ^ divisor) >>> 31) & 1) == 1 ? -1 : 1;
        long x = Math.abs((long) dividend);
        long y = Math.abs((long) divisor);
        long res = 0;
        //不采用累加,加法会溢出,采用相减的方式进行
        while (x >= y) {
            x -= y;
            res++;
        }
        res = symbol == 1 ? res : -res;
        //处理溢出
        if (res > Integer.MAX_VALUE || res < Integer.MIN_VALUE) {
            return Integer.MAX_VALUE;
        }
        return (int) res;
    }

二分查找 + 快速乘

快速乘是快速幂的变形,此处附上快速幂模版快速幂模版

public static int divide3(int dividend, int divisor) {
        int symbol = (((dividend ^ divisor) >>> 31) & 1) == 1 ? -1 : 1;
        long x = Math.abs((long) dividend);
        long y = Math.abs((long) divisor);
        long res = 0;
        if (x < y) {
            return 0;
        }
        long left = 1;
        long right = x;
        long mid;
        while (left <= right) {
            mid = ((right - left) >> 1) + left;
            long ans = quickAdd(y, mid);
            if (ans == x) {
                res = mid;
                break;
            } else if (ans > x) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        res = res == 0 ? right : res;
        res = symbol == 1 ? res : -res;
        if (res > Integer.MAX_VALUE || res < Integer.MIN_VALUE) {
            return Integer.MAX_VALUE;
        }
        return (int) res;
    }

    /**
     * 快速加法,m * n
     *
     * @param m
     * @param n
     * @return
     */
    public static long quickAdd(long m, long n) {
        long res = 0;
        while (n > 0) {
            if ((n & 1) == 1) {
                res += m;
            }
            m += m;
            n >>= 1;
        }
        return res;
    }

二分查找变形,查找右边界

public static int divide3(int dividend, int divisor) {
        int symbol = (((dividend ^ divisor) >>> 31) & 1) == 1 ? -1 : 1;
        long x = Math.abs((long) dividend);
        long y = Math.abs((long) divisor);
        long res = 0;
        if (x < y) {
            return 0;
        }
        long left = 1;
        long right = x;
        long mid;
        while (left < right) {
        	//注意此处要 + 1,+ 1的目的是为了找到右边界
            mid = (right + left + 1) >> 1;
            long ans = quickAdd(y, mid);
            //二分查找变形,查找右边界
            if (ans <= x) {
                left = mid;
            } else {
                right = mid - 1;
            }
        }
        res = symbol == 1 ? right : -right;
        if (res > Integer.MAX_VALUE || res < Integer.MIN_VALUE) {
            return Integer.MAX_VALUE;
        }
        return (int) res;
    }

同理,查找左边界

注意:左边界和右边界的区别在于找到的右边界一定<=x,而左边界可能=x,也可能>x

public int divide(int dividend, int divisor) {
        int symbol = (((dividend ^ divisor) >>> 31) & 1) == 1 ? -1 : 1;
        long x = Math.abs((long) dividend);
        long y = Math.abs((long) divisor);
        long res = 0;
        if (x < y) {
            return 0;
        }
        long left = 1;
        long right = x;
        long mid;
        while (left < right) {
            mid = (right + left) >> 1;
            long ans = quickAdd(y, mid);
            //二分查找变形
            if (ans >= x) {
                right = mid;
            } else {
                left = mid + 1;
            }
        }
        //查找左边界需要加上判断,为什么要加上判断?因为不一定存在right 使 y * right == x,如果一定存在则不用判断
        if(quickAdd(y,right) > x){
            right--;
        }
        res = symbol == 1 ? right:-right;
        if (res > Integer.MAX_VALUE || res < Integer.MIN_VALUE) {
            return Integer.MAX_VALUE;
        }
        return (int) res;
    }

方法三:不使用long型,类快速加运算

来自于leetcode作者:AC_OIer思路,采用倍增思想,x / y,目的在于寻找x中包含几个y,那么可以采用倍增思路,x包含一个y,那么x是否包含两个y?是否包含四个y?

dividend = 39 divisor = 2
39 - 32,相当于减去了16个2
dividend = 7 divisor = 2
7 - 4,相当于减去了2个2
dividend = 3 divisor = 2
3 - 2,相当于减去了1个2
得到答案19
class Solution {
    public static int divide(int a, int b) {
        int min = Integer.MIN_VALUE;
        int max = Integer.MAX_VALUE;
        //提前处理溢出
        if (a == min && b == -1) return max;
        //防止溢出
        int limit = min >> 1;
        //是否同号
        boolean flag = false;
        if (a > 0) {
            flag = !flag;
            a = -a;
        }
        if (b > 0) {
            flag = !flag;
            b = -b;
        }
        int res = 0;
        while (a <= b) {
            int temp = b;
            int count = 1;
            while (temp >= limit && temp >= a - temp) {
                temp += temp;
                count += count;
            }
            res += count;
            a -= temp;
        }
        return flag ? -res : res;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值