Leetcode:Divide Two Integers分析和实现
题目要求我们用一个32位整数整除另外一个整数,但是不允许我们使用除法,乘法和取模运算。
有趣的问题,下面说一下我的思路:
首先,先给出两个正整数除法运算的过程。假设a为被除数,而b为除数。在计算机中无符号整数除法div可以用下面的数学公式来表示:

即计算机除法中的a/b实际上是数学意义上a/b代表的有理数向下取整值。可以换一个方法来等价表示上面公式:

因此我们只需要能找到一个值c,满足下面条件即可:

但是我们不能从1到正无穷枚举c,因为如果a足够大且b足够小,那么c的值可能要上亿,上亿次的枚举消耗的时间非常可怕。但是我们不能使用乘法又该如何快速增大枚举值呢。这源于一个思路,v[0]=1,v[1]=v[0]+v[0],...,v[n]=v[n-1]+v[n-1]。发现了吗,v[i]=2^i,而32位整数绝对不会超过v[32],因此我们可以快速的利用v数组快速逼近c。
实际做法如下:
v and u are arrays with size 32
v[0] = 1, u[0] = b
limit = 0
for(; u[limit] < a; limit = limit + 1)
u[limit + 1] = u[limit ] + u[limit ]
v[limit + 1] = v[limit] + v[limit]
这样我们就得到一个数组u,并且保证了u[0], ..., u[limit - 1] < a,且u[limit] >= a,实际上u[i] = b * 2^i。但是我们又该如何能够借助这样一个数组u计算出最终的c?
由于每个整数在计算机中都是由二进制表示而成,因此c必然等于2^i1+2^i2+...+2^in,其中i1,...in互不相同并按增序排序。因此我们所要找的实际上是这样一组i1,i2,...,in。由于2^n=1+2^0+2^1+2^2+...+2^(n-1),因此我们能得知2^in>2^i1+2^i2+...+2^in-1,换句话说有2^in<=c<2^(in+1),等价的形式为u[in]=b*2^in<=b*c=a<b*2^(in+1)=u[in+1]。到了这一步我们就知道如何快速地决定in,而对于in-1的计算,可以通过u[in-1]=b*2^in-1<=b*(c-2^in)=a-u[in]<b*2^(in-1+1)=u[in-1+1]得出,推理过程如上。这样不断地计算下去,我们就可以将i1,i2,...,in全部计算出来。
用代码展示上面的结论:
r = a, c = 0
for(i = limit; r >= b; i--)
if(u[i] <= r)
r = r - u[i]
c = c + v[i]
综合上面我们已经得到了计算两个正整数的方式。上面这个算法的时间复杂度与空间复杂度均为常数O(1),因为不存在与输入相关联的冗余循环。
对于a,b均为负数的除法,有a/b=(-a)/(-b),因此可以直接用上述正整数除法的运算方式。对于a为负数的运算。计算机中对于带一个负数除法ndiv的定义如下:

但是我们不希望为带负数的重新定义一个新的算法,故我们要使用下面公式提供的计算c的方法:

故到了这里问题全面解决。当然这只是理论上的,实践上还会存在数值超出32位整数表示范围的情况,这需要读者自己对特殊情况进行处理。
最后提供一下AC代码,主要是需要对Integer.MIN_VALUE和越界做处理:
package cn.dalt.leetcode;
/**
* Created by dalt on 2017/6/21.
*/
public class DivideTwoIntegers {
public int divide(int dividend, int divisor) {
if (divisor == 0) {
throw new ArithmeticException();
}
if (divisor == Integer.MIN_VALUE) {
return dividend == Integer.MIN_VALUE ? 1 : 0;
}
if (dividend == Integer.MIN_VALUE) {
if (divisor == -1) {
return Integer.MAX_VALUE;
}
if (divisor == 1) {
return Integer.MIN_VALUE;
}
if (divisor < 0) {
return divide(dividend - divisor, divisor) + 1;
}
return divide(dividend + divisor, divisor) - 1;
}
if (divisor < 0) {
return divide(-dividend, -divisor);
}
if (dividend < 0) {
return -divide(-dividend, divisor);
}
return div(dividend, divisor);
}
/**
* Calculate floor(a/b)
*
* @param a a positive number
* @param b a positive number
* @return floor(a/b)
*/
public int div(int a, int b) {
int[] v = new int[32];
int[] u = new int[32];
v[0] = 1;
u[0] = b;
int limit = 0;
for (; u[limit] <= a && u[limit] > 0; limit++) {
u[limit + 1] = u[limit] + u[limit];
v[limit + 1] = v[limit] + v[limit];
}
int c = 0;
int r = a;
for (limit--; r >= b; limit--) {
if (r >= u[limit]) {
c += v[limit];
r -= u[limit];
}
}
return c;
}
}
Leetcode:Divide Two Integers分析和实现的更多相关文章
- [LeetCode] Divide Two Integers 两数相除
Divide two integers without using multiplication, division and mod operator. If it is overflow, retu ...
- LeetCode: Divide Two Integers 解题报告
Divide Two Integers Divide two integers without using multiplication, division and mod operator. SOL ...
- Leetcode Divide Two Integers
Divide two integers without using multiplication, division and mod operator. 不用乘.除.求余操作,返回两整数相除的结果,结 ...
- [LeetCode] Divide Two Integers( bit + 二分法 )
Divide two integers without using multiplication, division and mod operator. 常常出现大的负数,无法用abs()转换成正数的 ...
- leetcode Divide Two Integers python
class Solution(object): def divide(self, dividend, divisor): """ :type dividend: int ...
- leetcode面试准备:Divide Two Integers
leetcode面试准备:Divide Two Integers 1 题目 Divide two integers without using multiplication, division and ...
- [Leetcode][Python]29: Divide Two Integers
# -*- coding: utf8 -*-'''__author__ = 'dabay.wang@gmail.com' 29: Divide Two Integershttps://oj.leetc ...
- leetcode第28题--Divide Two Integers
Divide two integers without using multiplication, division and mod operator. 分析:题目意思很容易理解,就是不用乘除法和模运 ...
- 【一天一道LeetCode】#29. Divide Two Integers
一天一道LeetCode系列 (一)题目 Divide two integers without using multiplication, division and mod operator. If ...
随机推荐
- 用 WEKA 进行数据挖掘——第二章: 回归
回归 回归是最为简单易用的一种技术,但可能也是最不强大(这二者总是相伴而来,很有趣吧).此模型可以简单到只有一个输入变量和一个输出变量(在 Excel 中称为 Scatter 图形,或 OpenOff ...
- Kotlin For Gank.io (干货集中营Kotlin实现)
介绍 Kotlin,现在如火如荼,所以花了一点时间把之前的项目用Kotlin重构一下 原项目地址:https://github.com/onlyloveyd/GankIOClient 对应Kotlin ...
- 【MFC】picture控件 两种有细微差别的动态加载图片方法
摘自:http://www.jizhuomi.com/software/193.html VS2010/MFC编程入门之二十七(常用控件:图片控件Picture Control) 分类标签: 编程入门 ...
- 动态加载css、js引用
在js代码中动态的加载js.css文件的引用 function addJsCssByLink(type,url) { var doc=document; if(type="js") ...
- 剑指offer—第三章高质量代码(合并两个排序链表)
题目:输入员两个递增排序的链表,合并这两个链表并使新的链表中的结点仍然是按照递增排序的. 思路:首先,定义两个头节点分别为Head1和Head2的链表,然后比较第一个节点的值,如果是Head1-> ...
- AVAWEB学习笔记 ---- 系列文章
[JAVAWEB学习笔记]网上商城实战5:后台的功能模块 [JAVAWEB学习笔记]网上商城实战4:订单模块 [JAVAWEB学习笔记]网上商城实战3:购物模块和订单模块 [JAVAWEB学习笔记]网 ...
- winSCP连接FTP没有上传的权限
错误: 原因: ftp用户为 1)查看ubantu中FTP文件夹目录所有者及权限,发现ftpName用户对FTP文件夹的权限为 “r-x” ,仅有读,执行权限 2) chmod o=rwx ftp ...
- Hadoop体系结构之 Yarn
1.1 YARN 基本架构 YARN是Hadoop 2.0中的资源管理系统,它的基本设计思想是将MRv1中的JobTracker拆分成了两个独立的服务:一个全局的资源管理器ResourceManage ...
- his移植问题
报错信息 权限不够未截图 注意修改uploads文件权限chmod 777 uploads 修改域名 参照dxtzy项目,sourcetree备注
- Memcached数据存储方式
1. memcached的数据存储方式被称为Slab Allocator,其基本方式是: ①:先把内存分成很多Slab,这个大小是预先规定好的,已解决内存碎片的问题.分配给Slab的内存空间被称为Pa ...