LeetCode-不用加号的加法(常用二进制操作符)

一、题目描述

        设计一个函数把两个数字相加。不得使用 + 或者其他算术运算符。

示例:

        输入: a = 1, b = 1 输出: 2(不用加法)

        a、b均可能为负数

二、解题思路

        加法运算不能使用+,那么可以考虑使用二进制来实现。

1、二进制相关操作符

        用于整数之间的二进制表示进行的操作。一般是32位来表示,补码表示下进行,正数:原反补相同负数:补码=原码取反(反码)+1。最高位作为符号位,0表示正数,1表示负数。默认是有符号的。需要注意的是,在C语言中对于无符号整数(unsigned int、unsigned short等)的操作不是在补码表示下进行的,而是直接按照原码进行操作。因为无符号整数没有符号位,所以不涉及符号位扩展和补码运算。在进行运算时,不需要手动补全操作数,会自动按位操作两个操作数的对应位。

       (1)、按位与(&):两个对应位都是1,则结果位为1,否则为0,对两个二进制数的每一个对应位执行与操作。

            00000101 
        &  00001001
        ----------------
            00000001 

#include <stdio.h>

int main() 
{
        int num1 = 0b1011; // 二进制数00000000  00000000  00000000  00001011
        int num2 = 0b1001; // 二进制数00000000  00000000  00000000  00001001
        int a= num1 & num2; // 按位与操作

        int num3 = 5;// 二进制数00000000  00000000  00000000  00000101
        int num4 = 6;// 二进制数00000000  00000000  00000000  00000110
        int b= num3 & num4;

        int num5 = -5;
        //5的二进制表示:00000000  00000000  00000000  00000101(原码)
        //取反得到反码: 11111111 11111111 11111111 11111010
        //反码加1得到补码:11111111 11111111 11111111 11111011
        int num6 = -6;// 二进制数11111111 11111111 11111111 11111010
        int c= num5 & num6;
        
        printf("结果为十进制:%d\n", a);//结果为十进制:9  二进制:00000000  00000000  00000000  00001001
        printf("结果为十进制:%d\n", b);//结果为十进制:4  二进制:00000000  00000000  00000000  00000100
        printf("结果为十进制:%d\n", c);//结果为十进制:-6  二进制:11111111 11111111 11111111 11111010
        return 0;
 }

        (2)、 按位或(|):两个对应位如果有一个为1,则结果位为1,否则为0。

           00000101 
        |  00001001 
        ----------------
           00001101 

#include <stdio.h>
//8位二进制表示
int main() {
    int num1 = 5;  // 二进制表示为 00000101
    int num2 = 9;  // 二进制表示为 00001001
    int result = num1 | num2;  // 按位或操作

    printf("结果为十进制:%d\n", result);//结果为二进制:13 二进制:00001101
    return 0;
}

        (3)、按位异或(^):如果两个对应位不同则结果为1,相同则结果为0。

            00000101
        ^  00001001
        ----------------
           00001100 

#include <stdio.h>

int main() {
    int num1 = 5;  // 二进制表示为 00000101
    int num2 = 9;  // 二进制表示为 00001001
    int result = num1 ^ num2;  // 按位异或操作

    printf("结果为十进制:%d\n", result);//结果为十进制:12  二进制:00001100
    return 0;
}

        (4)、按位取反(~):对二进制数的每一位执行取反操作,0变为1,1变为0。

#include <stdio.h>

int main() {
    unsigned int num = 5;  // 二进制表示为 00000000 00000000 00000000 00000101
    unsigned int result = ~num;  // 按位取反操作

    printf("结果为:%u\n", result);//结果为:4294967290 // 二进制:11111111 11111111 11111111 11111010

    int num1 = 5;  // 二进制表示为 00000000 00000000 00000000 00000101
    int result1 = ~num;  // 按位取反操作

    printf("结果为:%d\n", result1);//结果为:-6// 二进制:11111111 11111111 11111111 11111010

    return 0;
}

        (5)、左移(<<):将一个二进制数的所有位向左移动指定的位数,右侧空出的位用0填充。

        无符号位:左移操作会将二进制数向左移动指定的位数,右侧空出的位用0填充。无符号整数没有符号位的概念,因此左移操作只是简单地将所有位左移,不会导致符号位改变
           00000000 00000000 00000000 00000101(5)
        <<29
        ---------------------------------------
           10100000 00000000 00000000 00000000 (结果为2684354560)

        有符号位:左移操作会将二进制数向左移动指定的位数,右侧空出的位用0填充。但如果左移操作超过了整数类型的位数,结果是未定义的行为。如果左移导致符号位的改变,则结果是未定义的行为。左移操作可能导致溢出或者符号位的改变。

对于32位整数类型(如int),最大值和最小值如下:

最大值:2147483647(十进制)或 01111111 11111111 11111111 11111111(二进制)
最小值:-2147483648(十进制)或 10000000 00000000 00000000 00000000(二进制)

当对一个带符号位的整数进行左移操作时,左移的位数超过了整数类型的位数(32位),就会导致左移超出范围。例如,对于一个32位整数类型,左移超过31位就会超出范围。这时,左移操作的结果就是未定义的,可能会导致符号位的改变或溢出。

不论正负,补码移动,移动之后再转原码输出。

#include <stdio.h>

int main() {
    int num = -5; // 二进制表示为 11111111 11111111 11111111 11111011
    int result = num << 5;

    printf("结果为十进制:%d\n",result);//结果为十进制:-160 
    //补码 11111111 11111111 11111111 0110000
    //反码 11111111 11111111 11111111 0101111
    //原码 10000000 0000000 0000000 1010000
    
    return 0;
}

        (6)、右移(>>):将一个二进制数的所有位向右移动指定的位数,左侧空出的位根据符号位或补码规则进行填充。

        无符号位:右移之后,高位补0。

        00000000 00000000 00000000 00000101(5)
        >>2
        ---------------------------------------
        00000000 00000000 00000000 00000001 (1)

        有符号位:高位按照符号位补全。

        正数:符号位为0,符号位不动,高位补0

        00000000 00000000 00000000 00000101(5)
        >>2
        ---------------------------------------
        00000000 00000000 00000000 00000001 (1)

        负数:符号位为1,符号位不动,高位补1

        11111111 11111111 11111111 11111011(-5原码转补码移动)
        >>2
        ---------------------------------------
        11111111 11111111 11111111 11111110 (补码转原码输出)

#include <stdio.h>

int main() {
    int num = -5; // 二进制表示为 11111111 11111111 11111111 11111011
    int result = num >> 2;

    printf("结果为十进制:%d\n",result);//结果为十进制:-2
    //补码 11111111 11111111 11111111 11111110
    //反码 11111111 11111111 11111111 11111101
    //原码 10000000 0000000 0000000 00000010
    
    return 0;
}

二、两个二进制数相加演示

例:8+9:

8的二进制表示为: 1000

9的二进制表示为: 1001

  1000  (8的二进制)
+ 1001  (9的二进制)
-------
 10001  (结果为17的二进制)

可以发现,两个二进制数对应位相加,0+0->0、1+1->写0进位1、1+0->1

由于要考虑进位,我们把该运算过程分为直接相加部分和进位部分

进位通过&实现:都为1结果为1,否则为0。

直接相加部分通过^实现:相同为0,不同为1。

代码如下:

int add(int a, int b){
    while(b!=0)                  //b为0时代表无进位,输出结果
    {
        int c=a&b;               // 计算进位位
        a=a^b;                   // 执行无进位加法
        b=(unsigned int)c<<1;    //将进位左移一位
    }
    return a;
}

在第三步时使用了无符号整型强制类型转换,确保左移操作的结果不受符号位影响。(考虑负数相加情况)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值