牛客网 剑指offer 不用加减乘除做加法

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

题目很简单,但解决起来还是有一定难度的,说起代替加减乘除的方式,一下子能想起来的有sizeof,二进制的位运算。

sizeof行不通之后,又琢磨位运算中的异或,1 ^ 1->0, 1 ^ 0->1, 0 ^ 1->1, 0 ^ 0->0。这个规则看起来最接近加法法则,但是缺乏进位,比如,我们如何区分1 ^ 1 和0 ^ 0带来的结果呢,前者有进位而后者没有,那么如何给1 ^ 1 标志出进位呢?

答案是&,按位与。只有1 & 1结果为1。我们又知道加法中进位的含义就是例如:16+15,6+5结果超过10,所以在前一位加1,结果为31。在二进制中也是一样,所以我们需要把代替进位的&运算结果先左移一位,再与异或运算的结果按位相加。

这里你又会问:不是程序中不能出现加法运算吗?那要怎么实现进位(num1&num2)<<1和不含进位的和num1^num2的相加呢?

答案是使用递归或者循环结构。递归就是在函数体内再次调用此函数,循环就是将同样的加法和进位程序写入循环体内,多次执行。两者的结束条件都是一样的,想一想,什么时候我们就不需要进行加法计算了呢?那就是在进位为0的时候,此时num1^num2就是最后的结果了。

递归程序如下:首先给carry和pre_sum赋初值,这两步也是后续递归时不断执行的操作。由于这个程序的本质还是要执行加法,所以最后返回的应该是pre_sum。

int Add(int num1, int num2){
        int carry=(num1&num2)<<1;//与运算
        int pre_sum=num1^num2;//异或运算
        //这里不能用while,为什么?
        //因为如果用while,则一直使用的是第一次的carry初值,不会跳出循环
        //除非在循环里改变carry的值
        if(carry!=0){
            pre_sum=Add(carry, pre_sum);
        }
        return pre_sum;
    }

另一种是循环:由此也可以看出递归和循环是如何等价的。递归语句之外的其他操作是会被重复执行的,而循环结构内的语句也是不断执行的。

int Add(int num1, int num2){
        //也可以使用while循环代替递归
        int carry=(num1&num2)<<1;//与运算
        int pre_sum=num1^num2;//异或运算
        while(carry!=0){
            int temp=carry;
            carry=(carry&pre_sum)<<1;
            pre_sum=temp^pre_sum;
        }
        return pre_sum;
    }
int Add(int num1, int num2){
        //再次简化,num1:carry; num2:pre_sum
        while(num1!=0){
            int temp=num1;
            num1=(num1&num2)<<1;
            num2=temp^num2;
        }
        return num2;

    }

其实还是很难懂的,这里趁热记下来,方便以后的复习和思维锻炼。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值