在新浪看到这个题:
#谷歌面试题# 写一个函数,求两个整数之和,要求在函数体内不得使用+、-、×、÷。这是一道考察发散思维的很有意思的题目,当习以为常的东西被限制使用的时候,如何突破常规去思考?对于这样的题目,比较实用的做法是做几个例子,然后根据例子来归纳总结,否则,单凭抽象思维很难找到思路。(陈利人:http://weibo.com/1915548291/yEx01mSvN)
之前好像在CSDN上做过类似的题目,现在整理一下代码:
/*思路:按位运算,两个数按位异或得到没有进位的和;
*按位与得到每一位的进位;
*将按位与的结果向左移一位再跟按位异或的结果相加
*就得到两数相加的和.
*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
//递归方式
int plus_recursion(int a, int b)
{
if ((a&b) == 0)//递归停止条件:两数按位相加没有进位
{
return (a^b);
}
return ( plus_recursion(a^b, (a&b)<<1) );
}
//循环方式
int plus_loop(int a, int b)
{
while (b != 0)//循环停止条件:两数按位相加没有进位
{
a = a^b;//a保存按位异或的结果
b = ((b^a)&b) << 1;//b保存按位与的结果再左移一位
}
return a;
}
int main()
{
int time = 20, a, b, sum1, sum2, error1 = 0, error2 = 0;
srand(clock());
printf(" Recuersion\t\t\t\tLoop\n");
while (time)
{
a = rand()%100 - 30;
b = rand()%100 - 30;
sum1 = plus_recursion(a, b);
sum2 = plus_loop(a, b);
printf("%-2d: %3d + %3d = %4d ",21 - time, a, b, sum1);
if (a + b == sum1)
{
printf("(√)\t");
}
else
{
printf("(×)\t");
error1 += 1;
}
printf("\t%3d + %3d = %4d ",a, b, sum2);
if (a + b == sum2)
{
printf("(√)\n");
}
else
{
printf("(×)\n");
error2 += 1;
}
time -= 1;
}
if (error1 == 0)
{
printf(" OH,YEAH!\t\t\t\t");
}
else
{
printf(" OH,SHIT! %d errors!\t\t\t\t", error1);
}
if (error2 == 0)
{
printf("OH,YEAH!\n");
}
else
{
printf("OH,SHIT! %d errors!\n", error2);
}
return 0;
}
输出:
Recuersion Loop
1 : 11 + 37 = 48 (√) 11 + 37 = 48 (√)
2 : 4 + -30 = -26 (√) 4 + -30 = -26 (√)
3 : 39 + -6 = 33 (√) 39 + -6 = 33 (√)
4 : 48 + 28 = 76 (√) 48 + 28 = 76 (√)
5 : 32 + 34 = 66 (√) 32 + 34 = 66 (√)
6 : -25 + 15 = -10 (√) -25 + 15 = -10 (√)
7 : 51 + -3 = 48 (√) 51 + -3 = 48 (√)
8 : 31 + 61 = 92 (√) 31 + 61 = 92 (√)
9 : 65 + 12 = 77 (√) 65 + 12 = 77 (√)
10: -3 + 6 = 3 (√) -3 + 6 = 3 (√)
11: 61 + -26 = 35 (√) 61 + -26 = 35 (√)
12: -28 + 23 = -5 (√) -28 + 23 = -5 (√)
13: 62 + 52 = 114 (√) 62 + 52 = 114 (√)
14: -9 + -14 = -23 (√) -9 + -14 = -23 (√)
15: -12 + 65 = 53 (√) -12 + 65 = 53 (√)
16: 17 + -4 = 13 (√) 17 + -4 = 13 (√)
17: 41 + 8 = 49 (√) 41 + 8 = 49 (√)
18: 39 + -18 = 21 (√) 39 + -18 = 21 (√)
19: 37 + 69 = 106 (√) 37 + 69 = 106 (√)
20: 5 + 64 = 69 (√) 5 + 64 = 69 (√)
OH,YEAH! OH,YEAH!
总结:
就像题目上描述的一样,当习以为常的东西被限制使用后该怎么办?
拿出笔,在纸上写下两个整数:当然是写成二进制的,按位运算是我仅学的一点知识里最直接想到的方法了。
0000 1011 + 0000 1001 = ? (0001 0100)
那要怎样按位运算来求和呢?无非就是“与、或、异或、同或”这四种运算的结合, 把这四种运算的结果都写出来看看:
0000 1011 & 0000 1001 = 0000 1001 0000 1011 | 0000 1001 = 0000 10110000 1011 ^ 0000 1001 = 0000 0010 0000 1011⊙ 0000 1001 = 1111 1101
计算的过程中就会发现(动笔画一画的好处就是在画的过程中或多或少能发现一些之前想不到的规律):“异或”跟加法很像,只不过没有进位,有进位就好办了,直接拿异或的结果跟进位相加就行了;那有没有办法找到进位呢?什么是进位,怎样才会出现进位? 1 跟 1相加的时候才有进位,好的,两个位都是 1 时会有进位,其他的情况都不会进位——“与”。观察一下“与”出来的结果,确实是我们想要的进位,只要把它左移一位跟异或出的结果相加就对了。
0000 0010 + 0001 0010 = ? 就是我们要的结果。
当然,不能直接用加法来加,那要怎么来加呢?会发现,现在的问题跟我们一开始画到纸上的问题是一模一样的!
那就重复上面的思路,将问题转化为:
0001 0000 + 0000 0100 = ?
直到最后转化成:
0001 0100 + 0000 0000 = ?
到这个地步发现不能再转化了,该停了。停下来怎么办?“和”要怎么求?——第一个数就是我们要求的“和”了。
以上便是递归的思路了。
我习惯于先用递归来理解和解决问题,然后再将递归换成循环,最后试着用循环来解释问题。
——————————————————————————————————————————————————————————————————
这个微博真的很不错的:@陈利人,里面有好些面试的知识值得学一学,过几天等找下工作就把这些知识整理成文档。
今天还看到一篇好文,分享一下:如何解逻辑推理题。(“放弃假设,逆推结果,分类分步,倒逼思路”通过试错和排除基本错误,迅速寻找答案。)