LeetCode之路:371. Sum of Two Integers
来源:互联网 发布:邮政网络银行 编辑:程序博客网 时间:2024/06/07 22:03
一、引言
这道题非常小巧,题干非常简洁:
Calculate the sum of two integers a and b, but you are not allowed to use the operator + and -.
Example:
Given a = 1 and b = 2, return 3.
翻译下,也就是一句话的事:计算两个整型数的和,要求不能使用 +
、-
运算符。
看到这里,最直接的想法应该就是,这是一道考察位运算的问题,接下来我们来一步一步解决这个问题。
二、让我们尝试着计算下两个二进制整数的和
既然要计算和了,那么我们也不能光想不实际算算,让我们实际看看用纸笔计算两个整型数的和需要哪些步骤:
1. 在草稿纸上写好两个二进制数
如图,我们首先将两个数转换成二进制数写在草稿纸上,注意一位一位对齐。
2. 我们就像计算十进制数的加法一样
如图,我们就像计算十进制数的加法一样,我们首先从最右边的开始,此时是 1 加 1,那么结果是 0 并且向前面进了 1 位,我们就在前面一位的上面标一个进位 1。如此这般计算下去,就得到了最后的结果。
这里建议读者一定要自己用纸和笔自己试着算一下二进制数的加法,这会对你进行这道题的解答有启发意义。
三、用程序模拟我们刚才的计算过程
通过实际的纸笔运算,我们已经了解了二进制整数的加法是如何进行的了。那么我们来总结下,究竟是如何进行的?
首先,我们计算同样位置的数的和,如果大于 2 ,则进 1 位,前 1 位标 1(想想我们可以怎么样模拟进位操作);
然后,我们到下一个位置上,进行同样的计算过程,如果大于 2 ,则进位,否则就不用进位直接相加(想想我们如何模拟直接相加操作);
最后,当我们算到了没有进位并且当前位置上的数相加为 0 时,则计算结束(同样想想我们该如何判定循环结束)。
如果你真的认真思考了的话,那么我们来一起探讨下,如何模拟上述的过程:
1. 模拟进位操作
我们看看进位操作的本质是什么?
同样位置上的数有两个相同的 1,然后向前位标 1。
想到了什么了吗?我们只需要进行按位与操作即可得到当前所有的同样位置有两个 1 的位,然后我们将结果左移 1 位即可实现这个操作。
2. 模拟直接相加操作
同样,我们来看看直接相加(不产生进位)操作的本质是什么?
我们仅仅是作了相加操作而已,同样位置上要么都是 0, 要么是 1 和 0(1 和 1 的情况被进位操作代替),然后我们直接算出了结果。
这个很容易模拟,我们只需要按位异或即可,当相同为 0,不同为 1。
3. 还有一步,我们如何合并进位的结果与直接相加的结果呢
现在是问题的关键了,我们只是算出了进位的结果,也算出了直接相加的结果,我们现在需要进行两者的结果的合并。
但是,现在问题来了,相加的结果可能还会出现同一位置上有两个 1 的情况,那怎么办呢?
很简单,我们继续回到第 1 步和第 2 步循环,我们可以将进位后的值赋值给第 1 个操作数,直接相加后的值赋值给第 2 个操作数。
到这里,很多人可能就想不明白了,那么什么时候才能算出最终的结果呢?
那么这里,我们要思考问题的关键是什么?
其实问题的关键就是我们何时能够判定循环结束,判定可以出结果。
让我们想想,当进位的结果和直接相加的结果都有了,我们是如何合并两者进行最终值的计算的呢?
我们是将这两个结果的同一位置每每进行对比,看是否全部都是可直接相加的(两个 0 或者 一个 1 和一个 0),如果出现了不可以直接相加还需要进位的(两个 1 )的情况出现,那么我们就无法进行直接的计算,需要进行循环进位合并结果,再看是否可以直接相加。
直到,直到我们可以直接相加,也就是用异或可以得到结果的时候,此时循环才算结束。
那么,代码已出:
// my solution , runtime = 0 msclass Solution {public: int getSum(int a, int b) { int temp = 0, sum = 0; do { sum = ((a & b) << 1) ^ (a ^ b); temp = (a & b) << 1; b = a ^ b; a = temp; } while (a & b); return sum; }};
思路都是上面讨论的思路,这里值得注意的是,这里我是如何判定出循环的呢?
当最后进位值和直接相加值可以直接合并的时候,也就是每个位上只有 0 和 0 或者 1 和 0 的时候,那么此时二者相与,必为 0;但是只要有一个位上有两个 1 ,二者相与就不为 0 值。
四、追求代码的极致简洁
当然了,没做完一道 LeetCode 上面的题目,你做出来答案可能只收获了一半,真正的收获可能是别人写的最高票答案:
// perfect solution , runtime = 0 msclass Solution {public: int getSum(int a, int b) { int sum = a; while (b != 0) { sum = a ^ b; b = (a & b) << 1; a = sum; } return sum; }};
这里可以看到,最高票答案也是用的同样的思路,只是代码更加简练。
这里,作者的判出条件跟我的不一样。
我是考虑的是合并进位值和直接相加值的相与值为 0 判出,而他则是考虑进位值为 0 判出,其实他这样的思路更加适合理解。
这里简要解释下作者的思路:首先,sum 存放每次循环中 a 与 b 的异或值,也就是直接相加值;b 存放每次的进位值,然后 a 存储 sum (也就是直接相加值)进入下一次循环(当进位值非空);当且仅当进位值为空时,用户的上一次循环中的 sum 已经是可以直接相加的异或结果了,此时得到结果,返回。
五、总结
又见位操作的题目。
对于位操作的题目,我建议是不要死记硬背,我们可以在有过实际的推理和思考经验后,稍微记住几个死的定理,但是理解才是最重要的。
程序的设计过程,其实非常严谨的体现了我们的思考过程,程序写的乱,也就证明我们的思路非常紊乱。
尽管题目简单,也需要认真分析,认真思考,这才是提高程序编写能力的唯一途径。
To be Stronger!
六、一点点福利补充
其实一直记着的,但是写完博客后加上工作又比较忙,又给搞忘了。
这道题的最高票答案有一份非常详尽的关于位操作的整理笔记,对于我们的技能提升非常有帮助(只不过全是英文的),这里附上地址:
A summary: how to use bit manipulation to solve problems easily and efficiently。
- Leetcode之路 371. Sum of Two Integers
- LeetCode之路:371. Sum of Two Integers
- LeetCode之Sum of Two Integers
- leetcode之371. Sum of Two Integers(C++解法)
- LeetCode 371. Sum of Two Integers
- 371. Sum of Two Integers LeetCode OJ
- [Leetcode]371. Sum of Two Integers
- LeetCode - 371. Sum of Two Integers
- <LeetCode OJ> 371. Sum of Two Integers
- LeetCode 371. Sum of Two Integers
- leetcode 371. Sum of Two Integers
- leetcode 371.Sum of Two Integers
- LeetCode 371. Sum of Two Integers
- 371. Sum of Two Integers(Leetcode)
- leetcode 371. Sum of Two Integers
- 【leetcode】371. Sum of Two Integers【E】
- [leetcode] 371. Sum of Two Integers
- leetcode.371. Sum of Two Integers
- openstack 创建虚拟机的时候报错: Failed to allocate the network(s), not rescheduling.].
- 上机五
- SQL Server 2008 / 2008 R2 定期自动备份数据库
- 计算机网络结构
- 使用逻辑卷管理器管理灵活存储
- LeetCode之路:371. Sum of Two Integers
- java中的java.lang.Class对象
- FreeMarker获取页面的session、request
- C#导出Excel表格失败
- I/O多路复用机制(一)
- sql server2008R2 备份所有数据库
- Struts2.5新版本中的action三种实现方法
- 图解Android
- oracle 10g要开启哪些服务