剑指 Offer 65. 不用加减乘除做加法

258 阅读2分钟

一、题目描述:

写一个函数,求两个整数之和,要求在函数体内不得使用 “+”、“-”、“*”、“/” 四则运算符号。

示例:

输入: a = 1, b = 1
输出: 2

提示:

a, b 均可能是负数或 0
结果不会溢出 32 位整数

二、思路分析:

这道题比较简单, 主要考的就是位运算

回顾

回顾下 计算机在处理 加法 的运算逻辑:

原码:

  • 最高位为符号位, 其余位数为这个数的二进制表示 反码:
  • 整数: 和原码一样
  • 负数: 反码是在其原码的基础上, 除了符号位数不变,其余位取反. 补码:
  • 整数: 和原码一样
  • 负数: 补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1)

剩下的就是 按位加,多出来的数字向更高位产生一个进位, 这样就完成一个二进制的加法

java 中 数字就是按补码的结果存储的, 使用补码的好处是, 减法也可以按加法的运算结果做。

思考这题

先看几个二进制的加法

0001
000001
010110

可以看到

  • 第0位的结果: 两个数的第0位的异或
  • 第1位的结果: 两个数的第1位的异或 在异或第0位的进位

设 a 二进制的第i位为 a[i], b 二进制的第i位为 b[i]
第i位的是否有进位表示为 n[i-1] 第i位的和 m[i]

则 a[i] + b[i] = n[i-1] + m[i]
进一步 a + b = n + m
其中 n 是进位和所组成的数字,m是和组成的数字

所以如果一直做转换,可以确定, 当转换的结果中 进位和全是0构成的时候,也就是说n=0的时候,m就是a+b的结果

三、AC 代码:

public class Main {

    static int add(int a, int b) {
        while(b != 0) {
            int c = a ^ b;      // 非进位和     n
            b = (a & b) << 1;   // 进位和      m
            a = c;
            // System.out.printf("%d, %d\n", a, b);
        }
        return a;
    }

    public static void main(String[] args) {
        System.out.println(add(5, 3));
        System.out.println(add(1, 1));
    }
}

四、思考:

经过多轮测试, 发现 如果判断条件是进位和=0比判断条件非进位和=0要多一轮循环, 所以有小伙伴知道是为啥呢? 欢迎评论留言😃