算法通关村第十一关——位运算实现加减乘除

news/2024/10/21 7:47:07/

在计算机中,位运算的效率比加减乘除效率更高。

1.位运算实现加法

力扣371题, 给你两个整数 ab ,不使用 运算符 + -,计算并返回两整数之和。

分析:不让用运算符,就只能使用位运算。先来看一下两位二进制相加的情况:

0 + 0 = 0
0 + 1 = 0
1 + 0 = 0
1 + 1 = 1 (此处发生了进位,结果应该为 10,可以用 1 << 1 来表示)

两数相加时,我们需要考虑的是进位部分的结果和不进位部分的结果。由上面的例子可以知道,对于ab两个数不进位部分的情况是:相同为0,不相同为1,也就是a ⊕ b。对于进位,其结果就是a & b也就是两位只有都是1的时候才会进位,进位发生的时候数的位数就变成两位了,我们只需要将其左移一位即可,也就是(a & b) << 1。综上得出两条结论:

  1. 不进位部分,计算就是a ⊕ b
  2. 对于进位部分,就是(a & b) << 1

代码如下:

function getSum(a, b) {while (b !== 0) {// 进位处运算就是 a & b。如果进位了,就左移一位let sign = (a & b) << 1;// 不进位处按异或进行位运算a = a ^ b;b = sign;}return a;
}

2.位运算实现乘法

力扣面试题08.05,递归乘法。 写一个递归函数,不使用*运算符, 实现两个正整数的相乘。可以使用加号、减号、位移,但要吝啬一些。

加入两个正整数分别是A B

分析:

  1. 首先,检查基本情况:如果乘数 B 等于0,那么乘积一定为0,所以返回0。
  2. 接下来,判断乘数 B 的二进制最低位是否为1(奇数检查),可以使用位运算 B & 1 来判断。如果最低位为1,说明 B 是奇数,进入第3步,否则进入第4步。
  3. 如果 B 是奇数,先将 A 加到结果中,然后通过递归计算 A * (B >> 1),这里的 B >> 1 表示将 B 右移一位,相当于除以2。递归结果再左移一位,相当于乘以2,然后将两者相加,即 A + (A * (B >> 1) * 2)
  4. 如果 B 是偶数,直接通过递归计算 A * (B >> 1),然后结果左移一位,即 A * (B >> 1) * 2

代码如下:

/*** 递归乘法:计算两个数的乘积* @param {number} A* @param {number} B* @return {number}*/
var multiply = function(A, B) {// 如果B为0,任何数乘以0都为0if (B === 0) {return 0;}// 检查B的二进制最低位是否都为1(奇数检查)if ((B & 1) === 1) {// 如果B是奇数,将A加到结果中,然后将A 乘以(B >> 1)的结果加倍return A + (multiply(A, B >> 1) << 1);} else {// 如果B是偶数,将A乘以(B >> 1)的结果加倍return multiply(A, B >> 1) << 1;}
};

3.位运算实现除法

力扣29题,给你两个整数,被除数 dividend 和除数 divisor(!== 0)。将两数相除,要求 不使用 乘法、除法和取余运算。

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

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

**注意:**假设我们的环境只能存储 32 位 有符号整数,其数值范围是$ [−2^{31}, 2^{31} − 1]$。本题中,如果商 严格大于 2 31 − 1 2^{31} − 1 2311 ,则返回 2 31 − 1 2^{31} − 1 2311;如果商 严格小于 − 2 31 -2^{31} 231 ,则返回 − 2 31 -2^{31} 231

分析:这道题有些复杂,细节比较多。刚开始我们最先想到的就是用循环减法,但是这样被除数一变大效率就太低。

首先对于溢出或者容易出错的边界情况进行讨论:

  • 当被除数为32位有符号整数的最小值 − 2 31 -2^{31} 231时:
    • 如果除数为1,直接返回 − 2 31 -2^{31} 231
    • 如果除数为-1,得到的是 2 31 2^{31} 231,会产生溢出,此时需要返回 2 31 − 1 2^{31}-1 2311
  • 当除数为32位有符号整数的最小值 − 2 31 -2^{31} 231时:
    • 如果被除数同样为 − 2 31 -2^{31} 231,直接返回1;
    • 对于其余的情况,返回0。
  • 如果被除数为0,返回0。

对于一般的情况,被除数和除数符号有可能不同,这样就会有4种情况,不便于编码。为了方便编码我们可以采取对被除数和除数取相反数的方法以统一符号,这样就只需要考虑一种情况。

为了防止正整数出现溢出的情况,把符号都统一为负数,在返回答案时也需要取相反数。

使用二分查找来解决,同样需要注意边界问题。被除数x和除数y都是负数,那么他们的商z就是正数,根据除法及余数的定义,可以将其改成乘法的等价形式: z ∗ y > = x > = ( z + 1 ) ∗ y z * y >= x >= (z + 1) * y zy>=x>=(z+1)y,

由于不让用除法就需要用到快速乘方法,本质上就是加法运算。由于较大的z也会导致加法运算溢出,例如判断A + B < C,由于都为负数,A + B可能会产生溢出,所以必须判断A < C - B是否成立。由于任意两个负数的差一定在 [ − 2 31 + 1 , 2 31 − 1 ] [-2^{31} + 1, 2^{31} - 1] [231+1,2311]范围内,这样就不会产生溢出:

完整代码如下:

function divide(divided, divisor) {const MAX_VALUE = 2**31 - 1;const MIN_VALUE = -(2**31);// 考虑被除数为最小值的情况if (divided === MIN_VALUE) {if (divisor === 1) {return MIN_VALUE;} else if (divisor === -1) {return MAX_VALUE;}}// 考虑除数为最小值的情况if (divisor === MIN_VALUE) {return divided === MIN_VALUE10;}// 考虑被除数为0的情况if (divided === 0) {return 0;}// 一般情况,使用二分查找// 将所有的正数取相反数,这样就只考虑一种情况let reversed = false;	// 标记是否取了相反数if (divided > 0) {divided = -divided;reversed = !reversed;}if (divisor > 0) {divisor = -divisor;reversed = !reversed;}let left = 0, right = MAX_VALUE, ans = 0;while (left <= right) {mid = left + ((right - left) >> 1);const check = quickAdd(divisor, mid, divided);if (check) {ans = mid;if (mid === MAX_VALUE) {break;}left = mid + 1;} else {rihgt = mid - 1;}}return reversed ? -ans : ans;
}/*** 快速乘* @params y* @params z* @params x* * */
// z * y >= x >= (z + 1) * y  (x和y为负数,z是正数)
function quickAdd(y, z, x) {// 需要判断 z * y >= x 是否成立let result = 0, add = y;while (z !== 0) {if ((z & 1) !== 0) {// 需要保证 result + add >= xif (result < x - add) {return false;}add += add;}if (z !== 1) {// 需要保证 add + add >= x,即两数之和大于等于第三个数,防止加法运算溢出if (add < x - add) {return false;}add += add;}// 不能使用除法,就用右移位运算z >>= 1;}return true;
}

http://www.ppmy.cn/news/1068895.html

相关文章

Apipost: 程序员必备的API管理神器

作为一款专为程序员打造的API管理工具&#xff0c;Apipost也成为开发人员圈子里的一款热门工具。Apipost拥有强大的功能和便捷操作性&#xff0c;这也让许多开发者爱不释手。那么&#xff0c;Apipost到底有哪些吸引人的特点呢&#xff1f;本文将为您详细介绍。 统一API管理 A…

新能源汽车技术的最新进展和未来趋势

文章目录 电池技术的进步智能驾驶与自动驾驶技术充电基础设施建设新能源汽车共享和智能交通未来趋势展望结论 &#x1f389;欢迎来到AIGC人工智能专栏~探索新能源汽车技术的最新进展和未来趋势 ☆* o(≧▽≦)o *☆嗨~我是IT陈寒&#x1f379;✨博客主页&#xff1a;IT陈寒的博客…

linux下安装Mycat

1 官网下载mycat 官方网站&#xff1a; 上海云业网络科技有限公司http://www.mycat.org.cn/ github地址&#xff1a; MyCATApache GitHubMyCATApache has 34 repositories available. Follow their code on GitHub.https://github.com/MyCATApache 2 Mycat安装 1 把MyCat…

PostgreSQL数据库定时备份脚本

大多数数据库管理系统都提供了自带的备份工具&#xff0c;可以使用这些工具来进行备份操作。 例如&#xff1a; MySQL&#xff1a;使用 mysqldump 命令进行备份。PostgreSQL&#xff1a;使用 pg_dump 命令进行备份。 以下是一个用于定时备份 PostgreSQL 数据库的示例脚本。这个…

【调整奇数偶数顺序】调整数组使奇数全部都位于偶数前面习题集讲解

题目&#xff1a; 题目名称&#xff1a; 调整奇数偶数顺序 题目内容&#xff1a; 调整数组使奇数全部都位于偶数前面。 输入一个整数数组&#xff0c;实现一个函数&#xff0c; 来调整该数组中数字的顺序使得数组中所有的奇数位于数组的前半部分&#xff0c; 所有偶数位…

构建个人博客_Obsidian_github.io_hexo

1 初衷 很早就开始分享文档&#xff0c;以技术类的为主&#xff0c;一开始是 MSN&#xff0c;博客&#xff0c;随着平台的更替&#xff0c;后来又用了 CSDN&#xff0c;知乎&#xff0c;简书…… 再后来是 Obsidian&#xff0c;飞书&#xff0c;Notion&#xff0c;常常有以下困…

prometheus+cadvisor监控docker容器

一、安装cadvisor docker pull google/cadvisor:latest二、运行容器 docker run -d \--volume/:/rootfs:ro \--volume/var/run:/var/run:rw \--volume/sys:/sys:ro \--volume/var/lib/docker/:/var/lib/docker:ro \--publish8088:8080 \--detachtrue \--namecadvisor \--priv…

无涯教程-分类算法 - Python实现函数

为了在Python中实现SVM&#xff0c;无涯教程将从标准库导入开始&#xff0c;如下所示- import numpy as np import matplotlib.pyplot as plt from scipy import stats import seaborn as sns; sns.set() 接下来&#xff0c;从sklearn.dataset.sample_generator创建具有线性可…