剑指offer01:整数除法

第一章整数:第一题

开门见山直接上题
1.输入两个int型整数,它们进行除法计算并返回商,要求不得使用乘号,除号/以及求余符号%*,当发出溢出时,返回最大的整数值。假设除数不为0。例如,输入15和2,输出15/2,即7。
(注意:int型范围是-2 ^31~-2 ^31-1)
分析:
不让用乘号和除号那就用加减试试呗,15减2再减2一直减到等于1,共耗费了7个2,余下个1,因此商是7,可以用循环实现这个过程,这么做虽然很简单,但存在问题,当被除数很大很大,除数很小时,减法的次数就太多了,比如说(2 ^31-1)/2,得执行2 ^31-1次,时间复杂度是O(n),那么有没有优化下这个解法呢?
对上面步骤稍做调整,上例子比较直观,15/2,15不仅大于2,而且大于2的2倍,2的4倍,但小于2的8倍也就是16,于是让15减去8,因为被减去的是8也就是那个2的四倍,所以这一部分的商就是4,接下来对剩余的7和除数2进行比较,7大于2,大于2的2倍,但小于2的4倍,因此减去4也就是2的2倍得3,这一部分的商则是2,再接下来对剩余的3和除数2比较,发现3大于2,小于2的2倍,因此减去2的一倍得1,这一部分的商是1,最后剩的1比除数2小,所以1是余数,再把每一部分的商合并就可以求出商是多少了。
再画张图更直观点:
在这里插入图片描述

再和之前那种一直减的解法相比时间复杂度又是如何呢?我们就是为了降低时间复杂度的!
被除数是n的话,除数是c,则n - c * 2 ^(k )<= 0 时,相当于执行了k次,k = log(n/c),这部分时间复杂度相当于logn,每个这种部分发生几次呢,由15到7到3到1,每次都类似于除以2,也相当于2的a次等于15,a=log(n),发生log(n)次,所以时间复杂度为两者相乘logn ^2.与之前那个时间复杂度n相比小。
还需要注意一些问题:
因为int型的范围是-2 ^31~-2 ^31-1,如果用-2 ^31除以-1就会导致溢出,也就是越界,所以这种情况我们算特殊情况。
由于无论什么类型,任意正数转换为负数都不会溢出,所以我们可以先将正数转化为负数,再用优化算法计算,最后根据需要调整正负号。
剩下的一些细节看代码注解即可
上代码:

package com.wzc;
//整数是一种基本的数据类型,编程语言可能会提供占据不同的内存空间的整数类型,每种类型的整数范围
//也不相同,例如java中有四种不同的整数类型,分别是8位的byte,16位的short,32位的int以及64
//位的long
/*
* 题目1:整数除法
* 输入两个int整数,它们进行除法计算并返回商,要求不能使用乘号*,除号/,以及余符号%,当发生
* 溢出时,返回最大的整数值,假设除数不为0,例如15和-2,15/-2=-7
* */
public class Divide {
    public static void main(String[] args) {
        int result = divide(15,-2);
        System.out.println(result);
    }
    public static int divide(int dividend,int divisor){
//        溢出情况
        if (dividend == 0x80000000 && divisor == -1){
            return Integer.MAX_VALUE;
        }
//       这个negative是用来判断最终结果正负号的
        int negative = 2;
//        将正数都转化为负数,因为任意正数转化为负数都不会溢出。
        if (dividend>0){
            negative--;
            dividend = -dividend;
        }
        if (divisor>0){
            negative--;
            divisor = -divisor;
        }
//        求商
        int result = divideCore(dividend,divisor);
        return negative == 1 ?-result : result;
    }

    private static int divideCore(int dividend, int divisor) {
        /*
        * 当被除数小于除数时,接着比较判断被除数是否小于除数的2倍,如果是就接着比较是否
        * 小于除数的四倍,以此类推如果被除数最多小于除数的2的k次倍时,那么被除数将减去除数
        * 的2的k次倍,然后将剩余的被除数重复前面的操作,由于每次都将除数翻倍,所以时间复杂度
        * 为logn
        * */
    //           result用来记录各个部分商的总和
        int result = 0;
        while(dividend <= divisor){
    //           每次第一部分商求出来后,将value再重置回除数,并将k重置为1,接着求下一部分商
            int value = divisor;
            int k =1;
//            为了防止int型dividend溢出,所以value值最多是-2的31次方的一半,所以得大于0xc0000000
            while (value >=0xc0000000 &&dividend <=value+value){
                k += k;
                value +=value;
            }
            
            result += k;
            dividend -= value;
        }
        return result;
    }
}

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龙崎流河

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值