写一个函数,求两个整数之和,要求在函数体内不得使用 “+”、“-”、“*”、“/” 四则运算符号。
看到这题的第一感觉就是使用位运算来解决,但是我对位运算相当不熟悉,于是一想就是2个小时(话说这个题为啥是简单题呀,感觉一点也不简单), 先回顾一下位运算:
| 符号 | 描述 | 运算规则 |
|---|---|---|
| & | 与 | 两个位都为1时,结果才为1 |
| | | 或 | 两个位都为0时,结果才为0 |
| ^ | 异或 | 两个位相同为0,相异为1 |
| ~ | 取反 | 0变1,1变0 |
| << | 左移 | 各二进位全部左移若干位,高位丢弃,低位补0 |
| >> | 右移 | 各二进位全部右移若干位,对无符号数,高位补0,有符号数,各编译器处理方法不一样,有的补符号位(算术右移),有的补0(逻辑右移) |
干看完全没有头绪,于是就使用笨方法,每个运算符都计算一遍,找找规律,最后发现两个点:
先来看下例子:
10 + 7
转成2进制相加:
1010 +
0111 =
10001
规律1:
1010 &
0111 =
0010
根据&的运算规则,都为1结果为1,正好二进制1+1需要进一位,然后<<运算符刚好可以做的进位操作
那么 0010 << 1 = 0100 (8) 可以得到需要进位的值
规律2:
1010 ^
0111 =
1101
根据^的运算规则,0 ^ 0 = 0 , 1 ^ 1 = 0 , 0 ^ 1 = 1 ;
正好和加法的到的结果一致,只是 1 + 1 = 10 进位的 1 没有算上,
而规律1可以得到进位的值,再和 ^ 得到的值相加可以了。
我们计算一下:
0100 +
1101 =
10001
刚好和预期一样.
于是我们可以的出结论:a + b = ((a & b) << 1) + (a ^ b) , 等式的右边也使用了 ”+“号,我们使用递归调用,随着递归的进行,进位的值会不断向高位推进,直到不需要进位,即 (a & b) << 1 == 0,这也是我们递归的结束条件。
最终代码:
func add(_ a: Int, _ b: Int) -> Int {
if a == 0 {
return b
}
return add((a & b) << 1 , a ^ b) //由于 << y优先级高于 & ,需要把 a & b 括起来
}
提交 -- 过啦!