第一章整数:第一题
开门见山直接上题
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 &÷nd <=value+value){
k += k;
value +=value;
}
result += k;
dividend -= value;
}
return result;
}
}