Java实现两数相除

题意

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

整数除法应该向零截断,也就是截去(truncate)其小数部分。例如,8.345 将被截断为 8 ,-2.7335 将被截断至 -2 。

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

难度

中等

示例

输入: dividend = 10, divisor = 3

输出: 3

解释: 10/3 = 3.33333.. ,向零截断后得到 3 。

输入: dividend = 7, divisor = -3

输出: -2

解释: 7/-3 = -2.33333.. ,向零截断后得到 -2 。

分析 1

题目要求不能用除法(/),也不能用乘法(*),还有 “取余(%)”,如果我们偏要用除法会怎么样呢?

class Solution {public int divide(int dividend, int divisor) {// 直接使用除法计算结果long result = (long) dividend / divisor;// 处理溢出情况if (result > Integer.MAX_VALUE) {return Integer.MAX_VALUE;}if (result < Integer.MIN_VALUE) {return Integer.MIN_VALUE;}return (int) result;}}

来看一下运行结果:

其实有时候我们真解不出来的话,多多少少先把答案写上去,运行通过,但遇到大数就不会成功。

分析 2

题目要求不能用除法,不过我们处理溢出情况的写法,后面还是要用到的,尤其是 (long) dividend 这个强转的处理,其实就考察了基本数据类型的转换问题,包括最后返回的 (int) result,都是很细节的问题,但却很重要。

那既然不能用除法,我们应该怎么去计算两数相除呢?

用减法

除法基本上是减法的逆运算,它表示将一个数(被除数)分成几个相等的部分(除数)时,可以得到多少个这样的部分(商)。如果用减法来实现除法,本质上是在问:“我可以从被除数中减去多少次除数?”每减去一次,就相当于得到了一个单位的商。

假设我们要计算 dividend / divisor = quotient(被除数 / 除数 = 商),其中 dividend 和 divisor 是已知的,我们要找到 quotient。

①、初始化计数器:设置一个计数器 count,初始值为 0。这个计数器将用来记录我们能从被除数中减去多少次除数。

②、循环减除数:只要被除数 dividend 大于或等于除数 divisor,就从被除数中减去除数,并且计数器 count 加一。

while (dividend >= divisor) {dividend -= divisor;count++;
}

③、处理剩余部分:当被除数小于除数时,循环结束。此时,count 的值就是整数除法的结果(商),而此时的 dividend 是除法的余数。

假设我们要计算 10 / 3:

  1. 初始时,dividend = 10,divisor = 3,count = 0。
  2. 10 >= 3,执行减法 10 - 3 = 7,count = 1。
  3. 7 >= 3,再次执行减法 7 - 3 = 4,count = 2。
  4. 4 >= 3,再次执行减法 4 - 3 = 1,count = 3。
  5. 此时,1 < 3,循环结束,count = 3 是商,1 是余数。

我们来试一下,直接用 ACM 的模式在 Intellij IDEA 中打印结果:

public class TwoDivide {public static void main(String[] args) {//        int divide = divide1(10, 3);
//        System.out.println(divide);System.out.println(divide1(10, 3));System.out.println(divide1(7, -3));System.out.println(divide1(0, 1));System.out.println(divide1(1, 1));System.out.println(divide1(1, 0));System.out.println(divide1(-2147483648, -1));}public static int  divide1( int dividend , int divisor){// 处理特殊情况if (dividend == Integer.MIN_VALUE && divisor == -1) {return Integer.MAX_VALUE;}// 符号处理boolean negative = (dividend < 0) ^ (divisor < 0);long ldividend = Math.abs((long)dividend);long ldivisor = Math.abs((long)divisor);long result = 0;while (ldividend >= ldivisor) {ldividend -= ldivisor;result++;}return negative ? (int)-result : (int)result;}

很遗憾,前 5 个测试用例都是可以顺利通过的,但最后 1 个测试用例耗时会特别长,因为要计算 2147483648 / 1次,这个数太大了,所以我们要想办法优化一下。

优化减法实现除法的关键在于减少需要执行的减法操作次数,对吧?

我们可以结合减法和倍增思想,每次减去 divisor 的倍数,这样就可以减少减法的次数。

  1. 初始时,令一个临时除数等于除数,每次尝试从被除数中减去临时除数。
  2. 如果可以减去,更新被除数(减去临时除数),临时除数倍增(乘以 2),同时记录下减去的次数。
  3. 如果不可以减去,则临时除数回退到除数,继续尝试减去直到被除数小于除数。

   public static int  divide2( int dividend , int divisor){// 处理特殊情况if (dividend == Integer.MIN_VALUE && divisor == -1) {return Integer.MAX_VALUE;}// 符号处理boolean negative = (dividend < 0) ^ (divisor < 0);long ldividend = Math.abs((long)dividend);long ldivisor = Math.abs((long)divisor);long result = 0;while (ldividend >=ldivisor){long tempDivisor = ldivisor , multilpe  = 1;//将除数加倍,并记录加的倍数while (ldividend >= tempDivisor+tempDivisor){tempDivisor+= tempDivisor; //加倍除数multilpe+= multilpe;  //累加倍数}ldividend-=tempDivisor; //减去加倍后的除数result+=multilpe;}return negative ? (int)-result : (int)result;}

内层 while 循环,确定能够从被除数中减去的最大的除数的倍数。

这个循环通过不断地加倍除数(乘以 2),寻找最接近且不超过被除数的那个除数的倍数。这个过程实际上是在模拟除法的过程,尝试找到一个合适的倍数,使得除数 × 倍数 ≤ 被除数,且尽可能地大。

while (ldividend >= tempDivisor + tempDivisor) {tempDivisor += tempDivisor; // 加倍除数multiple += multiple; // 记录加倍次数
}
  1. tempDivisor 从原始的除数开始,每次循环加倍(相当于tempDivisor * 2),试图接近但不超过 ldividend。
  2. multiple 记录了加倍的次数,也就是说,如果 tempDivisor 加倍了,那么 multiple 也加倍,表示除数增加了多少倍。

外层 while 循环,使用上一个循环确定的最大倍数来更新被除数,并累加到最终结果中。

当第一个循环结束后,我们找到了最接近且不超过被除数的那个除数的倍数。然后,我们从被除数中减去这个加倍后的除数,并将对应的倍数累加到最终结果中。如果更新后的被除数仍然大于或等于原始除数,这个过程会重复进行。

ldividend-=tempDivisor;//减去加倍后的除数
result+=multiple;//累加倍数

  1. ldividend -= tempDivisor;更新被除数,减去了找到的最大可减的加倍后的除数。
  2. result += multiple;将这一步中减去的除数的倍数累加到 result 中,因为这表示了我们在这一步中“除以了多少个”除数。

来看一下运行效率,还挺高:

分析 3

减法和倍增的另外一种变体就是使用位移,我们来看一下题解:

    public int divide3(int dividend, int divisor) {if (dividend == Integer.MIN_VALUE && divisor == -1) {return Integer.MAX_VALUE; // 防止溢出}boolean negative = (dividend < 0) ^ (divisor < 0);long ldividend = Math.abs((long)dividend);long ldivisor = Math.abs((long)divisor);long result = 0;while (ldividend >= ldivisor) {long temp = ldivisor, multiple = 1;while (ldividend >= (temp << 1)) {temp <<= 1;multiple <<= 1;}ldividend -= temp;result += multiple;}return negative ? (int)-result : (int)result;}

在二进制表示中,每向左移动一位,其实就等价于将这个数乘以 2。所以,我们可以通过位移来实现倍增。

while (ldividend >= (temp << 1)) {temp <<= 1;multiple <<= 1;
}

假设 temp 的值为 3,其二进制表示为 0011(这里为了简化,只展示了四位二进制)。当执行 temp << 1 操作时,temp 的二进制表示会向左移动一位,变为 0110,此时 temp 的值就变成了 6。可以看到,3 * 2 = 6,这正是位左移一位的效果。

分析 4

我们还可以使用二分法来实现除法,这个方法是最优的,也是最常用的。

二分法的思路是:我们可以将除数不断地向左移位,直到它大于被除数。因为这样可以最大限度地减小被除数,使得被除数减去除数的次数最少。

我们来看一下题解:

class Solution {public int divide(int dividend, int divisor) {// 处理特殊情况:Integer.MIN_VALUE除以-1会溢出if (dividend == Integer.MIN_VALUE && divisor == -1) {return Integer.MAX_VALUE;}// 使用异或运算确定结果的正负性,不同号为负boolean negative = (dividend < 0) ^ (divisor < 0);// 将被除数和除数都转换为正数,使用long类型避免溢出long ldividend = Math.abs((long)dividend);long ldivisor = Math.abs((long)divisor);// 初始化结果变量long result = 0;// 从最高位开始检查,对32位整数而言,这是31位for (int i = 31; i >= 0; i--) {// 检查ldividend右移i位后是否仍然大于等于ldivisorif ((ldividend >> i) >= ldivisor) {// 如果是,说明在2^i的位置上有一个ldivisor,将2^i累加到结果中result += 1L << i;// 同时,从ldividend中减去ldivisor左移i位的值,即减去了ldivisor的2^i倍ldividend -= ldivisor << i;}}// 根据之前确定的正负性,返回正确的结果return negative ? (int)-result : (int)result;}
}
public int divide4(int dividend, int divisor) {if (dividend == Integer.MIN_VALUE && divisor == -1) {return Integer.MAX_VALUE; // 防止溢出}boolean negative = (dividend < 0) ^ (divisor < 0);long ldividend = Math.abs((long)dividend);long ldivisor = Math.abs((long)divisor);long result = 0;//从最高位开始检查,对32位整数而言,这是31位for (int i  = 31 ;i >=0 ; i--){//检查ldividend右移i位后是否仍大于等于ldivisorif (ldividend>>i >= ldivisor){//如果是,说明在i^2的位置上有一个ldivisor,将i^2累加到结果中result+=1 << i ;ldividend-= ldivisor << i;}}// 根据之前确定的正负性,返回正确的结果return negative ? (int)-result : (int)result;}

这个题解虽然没有直接采用传统的二分搜索算法框架,但它实质上利用了二分思想的核心——每次操作都在减少搜索范围,逐步逼近目标值。

在这个问题中,目标是找到一个数x,使得x * divisor ≈ dividend。通过位操作,每次将dividend右移,实际上是在缩小搜索范围。

这个过程可以看作是对商的二进制表示的逐位确认:

  1. 高位到低位逼近:从dividend的最高位开始,逐步向下检查每一位,看在当前位上divisor是否能"放入"。这里,“放入”的意思是,看乘以2^i(通过左移i位实现)后的divisor是否不超过当前的dividend。这相当于在二分搜索的每一步中,决定这一位上的商是0还是1。
  2. 动态调整搜索范围:通过不断地调整ldividend(即减去已经确定能够放入的divisor的倍数),实际上是在缩小搜索范围,因为每次操作后的ldividend代表了尚未被除尽的余数。

总结

在数学中,加法是加减乘除的基础,减法是加法的逆运算,乘法是加法的倍增,除法是乘法的逆运算。

两数相乘,可以用加法来实现,两数相除,可以用减法来实现,这是最基本的思路。

比如说 10/2,我们可以用 10-2-2-2-2-2=0 来实现,这样就得到了商 5。

比如说 10/3,我们可以用 10-3-3-3-1=0 来实现,这样就得到了商 3 余 1。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/791596.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

leetcode 热题 100(部分)C/C++

leetcode 热题 100 双指针 盛最多水的容器 【mid】【双指针】 思路&#xff1a; 好久没写代码sb了&#xff0c;加上之前写的双指针并不多&#xff0c;以及有点思维定势了。我对双指针比较刻板的印象一直是两层for循环i&#xff0c;j&#xff0c;初始时i,j都位于左界附近&…

Open CASCADE学习|刚体( TopoDS_Shape)按某种轨迹运动,停在指定位置上

今天实现如下功能&#xff1a;刚体做做螺旋运动&#xff0c;轨迹已知&#xff0c;求刚体在每个位置上的所占据的空间&#xff0c;就是把刚体从初始位置变换到该位置。 这里的刚体是一个砂轮截面&#xff0c;螺旋运动轨迹由B样条曲线拟合&#xff0c;通过Frenet标架确定运动轨迹…

iOS使用CoreML运用小型深度神经网络架构对图像进行解析

查找一个图片选择器 我用的是ImagePicker 项目有点老了&#xff0c;需要做一些改造&#xff0c;下面是新的仓库 platform :ios, 16.0use_frameworks!target learnings dosource https://github.com/CocoaPods/Specs.gitpod ImagePicker, :git > https://github.com/KevinS…

Python之Opencv进阶教程(1):图片模糊

1、Opencv提供了多种模糊图片的方法 加载原始未经模糊处理的图片 import cv2 as cvimg cv.imread(../Resources/Photos/girl.jpg) cv.imshow(girl, img)1.1 平均值 关键代码 # Averaging 平均值 average cv.blur(img, (3, 3)) cv.imshow(Average Blur, average)实现效果 1.2…

STM32F407 FSMC并口读取AD7606

先贴一下最终效果图.这个是AD7606并口读取数据一个周期后的数据结果. 原始波形用示波器看是很平滑的. AD7606不知为何就会出现干扰, 我猜测可能是数字信号干扰导致的. 因为干扰的波形很有规律. 这种现象基本上可以排除是程序问题. 应该是干扰或者数字信号干扰,或者是数字和模拟…

基于Spring Boot的餐厅点餐系统

基于Spring Boot的餐厅点餐系统 开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;Maven3.3.9 部分系统展示 管理员登录界面 用户注册登录界面 …

​如何使用ArcGIS Pro进行洪水淹没分析

洪水淹没分析是一种常见的水文地理信息系统应用&#xff0c;用于模拟和预测洪水事件中可能受到淹没影响的地区&#xff0c;这里为大家介绍一下ArcGIS Pro进行洪水淹没分析的方法&#xff0c;希望能对你有所帮助。 数据来源 教程所使用的数据是从水经微图中下载的DEM数据&…

Python学习笔记-Flask接收post请求数据并存储数据库

1.引包 from flask import Flask, request, jsonify from flask_sqlalchemy import SQLAlchemy 2.配置连接,替换为自己的MySQL 数据库的实际用户名、密码和数据库名 app Flask(__name__) #创建应用实列 app.config[SQLALCHEMY_DATABASE_URI] mysqlpymysql://ro…

鸿蒙OS开发实例:【应用事件打点】

简介 传统的日志系统里汇聚了整个设备上所有程序运行的过程流水日志&#xff0c;难以识别其中的关键信息。因此&#xff0c;应用开发者需要一种数据打点机制&#xff0c;用来评估如访问数、日活、用户操作习惯以及影响用户使用的关键因素等关键信息。 HiAppEvent是在系统层面…

适用于 Linux 的 Windows 子系统安装初体验

1、简述 Windows Subsystem for Linux (WSL) 是 Windows 的一项功能&#xff0c;允许您在 Windows 计算机上运行 Linux 环境&#xff0c;而无需单独的虚拟机或双重启动。 WSL 旨在为想要同时使用 Windows 和 Linux 的开发人员提供无缝且高效的体验。 使用 WSL 安装和运行各种 L…

【javaScript】DOM编程入门

一、什么是DOM编程 概念&#xff1a;DOM(Document Object Model)编程就是使用document对象的API完成对网页HTML文档进行动态修改&#xff0c;以实现网页数据和样式动态变化的编程 为什么要由DOM编程来动态修改呢&#xff1f;我们就得先理解网页的运行原理&#xff1a; 如上图&a…

IO流:字节流、字符流、缓冲流、转换流、数据流、序列化流 --Java学习笔记

目录 IO流 IO流的分类 IO流的体系 字节流&#xff1a; 1、Filelnputstream(文件字节输入流) 2、FileOutputStream(文件字节输出流) 字节流非常适合做一切文件的复制操作 复制案例&#xff1a; try-catch-finally 和 try-with-resource 字符流 1、FileReader(文件字符…

ALPHA开发板上的PHY芯片驱动:LAN8720驱动

一. 简介 前面文章了解到&#xff0c;Linux内核是有提供 PHY通用驱动的。 本文来简单了解一下ALPHA开发板上的 PHY网络芯片LAN8720的驱动。是 LAN8720芯片的公司提供的 PHY驱动。 二. ALPHA开发板上的PHY芯片驱动&#xff1a;LAN8720驱动 我 们 来 看 一 下 LAN8720A 的 …

输入url到页面显示过程的优化

浏览器架构 线程&#xff1a;操作系统能够进行运算调度的最小单位。 进程&#xff1a;操作系统最核心的就是进程&#xff0c;他是操作系统进行资源分配和调度的基本单位。 一个进程就是一个程序的运行实例。启动一个程序的时候&#xff0c;操作系统会为该程序创建一块内存&a…

HDLbits 刷题 --Always nolatches

学习: Your circuit has one 16-bit input, and four outputs. Build this circuit that recognizes these four scancodes and asserts the correct output. To avoid creating latches, all outputs must be assigned a value in all possible conditions (See also always…

【HTML】简单制作一个3D动画效果重叠圆环

目录 前言 开始 HTML部分 CSS部分 效果图 总结 前言 无需多言&#xff0c;本文将详细介绍一段代码&#xff0c;具体内容如下&#xff1a; 开始 首先新建文件夹&#xff0c;创建两个文本文档&#xff0c;其中HTML的文件名改为[index.html]&#xff0c;CSS的…

搞学术研究好用免费的学术版ChatGPT网站-学术AI

学术版ChatGPThttps://chat.uaskgpt.com/mobile/?user_sn88&channelcsdn&scenelogin 推荐一个非常适合中国本科硕士博士等学生老师使用的学术版ChatGPT&#xff0c; 对接了超大型学术模型&#xff0c;利用AI技术实现学术润色、中英文翻译&#xff0c;学术纠错&#…

centOS如何升级python

centOS下升级python版本的详细步骤 1、可利用linux自带下载工具wget下载&#xff0c;如下所示&#xff1a; 笔者安装的是最小centos系统&#xff0c;所以使用编译命令前&#xff0c;必须安装wget服务&#xff0c;读者如果安装的是界面centos系统&#xff0c;或者使用过编译工具…

在 Amazon Timestream 上通过时序数据机器学习进行预测分析

由于不断变化的需求和现代化基础设施的动态性质&#xff0c;为大型应用程序规划容量可能会非常困难。例如&#xff0c;传统的反应式方法依赖于某些 DevOps 指标&#xff08;如 CPU 和内存&#xff09;的静态阈值&#xff0c;而这些指标在这样的环境中并不足以解决问题。在这篇文…

Stable Diffusion 本地化部署

一、前言 最近在家背八股文背诵得快吐了&#xff0c;烦闷的时候&#xff0c;看到使用 AI 进行作图&#xff0c;可以使用本地话部署。刚好自己家里的电脑&#xff0c;之前买来玩暗黑4&#xff0c;配置相对来说来可以&#xff0c;就拿来试试。 此篇是按照 Github 上的 stable-d…