【算法】比特位计算(A+B Problem)-位运算符介绍、位运算实现加减乘除

问题描述

问题:计算A+B,不适用“+”运算符(LintCode 1.A + B Problem)
注意:A B均为32位整数,可使用比特位计算

解决思路

算法示例

代码如下:(通过LintCode测试)

class Solution {
public:
    /*
     * @param : An integer
     * @param : An integer
     * @return: The sum of a and b
     */
    int aplusb(int a, int b) {
        // write your code here
        int s, c;  
        while(b != 0)  
        {  
            s = a^b;  
            c = (a&b)<<1;  
            a = s;  
            b = c;  
        }  
        return a;  
    }
};

知识整理

位运算

位运算是按照二进制进行的运算,c语言提供6个位运算操作符,这些操作符适用于整型操作数。具体6个操作符如下:

符号名称含义
&按位与两个相应的二进制位都为1,该位的结果值为1
|按位或两个相应的二进制位中只要有一个为1,该位的结果值为1
^按位异或两个相应的二进制位值相同则为0,否则为1
~取反一元运算符,对一个二进制数按位取反,将0变1,将1变0
<<左移将一个数的各二进制位全部左移N位,右补0
>>右移将一个数的各二进制位右移N位,移到右端的低位被舍弃,无符号数高位补0
1.按位与 &

参与运算的两个数按照二进制位依次进行与运算,即两位都是1结果为1,否则为0。例如:3&6,换算为8位二进制数(高位补0),为
00000011&
00000110=
00000010

按位与可用于:清零、保留指定位
清零:想将原数清零,只需要原数中所有为1的位,在新数位相应为0,两数&操作即可为00000000。
保留指定位:想保留原数的第n位,只要新数的第n位保证为1,两数&操作即可保留原数的第n位,想保留多位同理。

2.按位或 |

参与运算的两个数对应的二进制位只要有一个为1,结果为1。例如
00011001 |
01001010=
01011011

按位或可用于:将原数的某些位定值为1,只需要与它进行按位或操作的新数对应的位数为1即可。

3.按位异或 ^

参与运算的两个数对应二进制位相同为0,不同为1。例如:
00110011 ^
01000110 =
01110101

按位异或可用于:指定位翻转(与1异或)、保留原值(与0异或)、交换两数
交换两数:交换a和b:
a=a∧b;
b=b∧a;
a=a∧b; 交换成功

4.取反 ~

二进制位1变0,0变1。

5.左移 <<

根据指定位数将原数各个二进制位左移指定位,高位左移溢出则舍弃,低位空位则用0补齐。

当该数左移时溢出舍弃的高位中不包含1时,左移类似乘法,左移n位相当于该数乘以2^n

6.右移 >>

根据指定位数将原数各个二进制位右移指定位,移到右端的低位被舍弃。左端高位,如果无符号数,补0。对于有符号数,移入0称为逻辑右移,移入1的称为算术右移

位运算实现加减乘除

1.位运算实现加法

无论哪种进制的加法,其核心都是“和”、“进位”。位运算有一个特点,首先是:
位运算的异或运算与“和”一致
异或 1^1=0;1^0=1;0^0=0
求和 1+1=0;1+0=1;0+0=0
位运算的与运算与“进位”一致
位与 1&1=1;1&0=0;0&0=0
进位 1+1=1;1+0=0;0+0=0

综合上述两个特点实现加法,代码如下:(分析详见注释

int add(int a, int b) //递归形式
{
    if(b==0) //递归结束条件:如果右加数为0,即不再有进位,结束运算
        return a;
    int s = a^b;
    int c = (a&b)<<1; //进位左移1位
    return add(s, c); //把'和'和'进位'相加
}

int add(int a, int b) //循环形式
{
    int s, c;
    while(b != 0)
    {
        s = a^b;
        c = (a&b)<<1;
        a = s;
        b = c;
    }
    return a;
}
2.位运算实现减法

减法实质使用加法实现的。先把减数求负,然后做加法。对一个数求负的方法是:将该数连符号位一起取反,然后加一
代码如下:(先求负,在相加)

int negtive(int i) //求负
{
    return add(~i, 1);
}

int subtraction(int a, int b) //减法运算:利用求负操作和加法操作
{
    return add(a, negtive(b));
}
3.位运算实现乘法

乘法需要考虑符号位,需要进行符号位的提取,以及负数的取正。乘法的实现也可借助加法,a*b可以看成b个a相加,通过循环实现,代码如下:(此法慢,其他方法不做赘述)

int getsign(int i){ //取一个数的符号,看是正还是负
    return (i>>31);
}

int bepositive(int i){ //将一个数变为正数,如果本来就是正,则不变;如果是负,则变为相反数。注意对于-2147483648,求负会溢出。
    if(i>>31)
        return negtive(i);
    else
        return i;
}

int multiply(int a, int b){ //循环法实现加法
    bool flag = true;
    if(getsign(a) == getsign(b)) //积的符号判定
        flag = false;

    a = bepositive(a);//先把乘数和被乘数变为正数
    b = bepositive(b);
    int ans = 0;
    while(b){
        ans = add(ans, a);
        b = subtraction(b, 1);
    }

    if(flag)
        ans = negtive(ans);
    return ans;
}
4.位运算实现除法

除法可借助减法实现,从被除数上减去除数,看能减多少次之后变得不够减。代码如下:(此法慢,其他方法不做赘述)

int division(int a, int b){
    if(b==0)
        return 0;

    bool flag = true;
    if(getsign(a) == getsign(b)) //积的符号判定
        flag = false;

    a = bepositive(a);
    b = bepositive(b);

    int n = 0;
    a = subtraction(a, b);
    while(a>=0){
        n = add(n, 1);
        a = subtraction(a, b);
    }

    if(flag)
        n = negtive(n);
    return n;
}

位运算常用技巧

1.计算二进制数中1的个数

通过与初始值为1的标志位进行与运算,判断最低位是否为1;然后将标志位左移,判断次低位是否为1;一直这样计算,直到将每一位都判断完毕。

int countOf1(int num){
    int count = 0;
    unsigned int flag = 1;
    while(flag){
        if(num & flag)
            count++;
        flag = flag << 1;
    }
    return count;
}
2.判断某数是否为2的n次方

一个数是2的n次方,则这个数的最高位是1,其余位为0。

bool is2Power(int num){
    bool flag = true;
    num = num & (num - 1); //计算num和num - 1的与的结果
    if(num) //如果结果为0,则不是2的n次方   
        flag = false;   
    return flag;
}
3.判断某数的奇偶性

判断奇偶性,实质是判断最后一位是否是1。

// 判断一个数的奇偶性.返回1,为奇数;返回0,为偶数
bool isOdd(int num){
    return num & 1 == 1;
}
4.交换两数

交换a和b:
a=a∧b;
b=b∧a;
a=a∧b;

5.求绝对值

将原数n右移31位,可以获得n的符号。

// 若n为正数,得到0;若n为负数,得到-1 
int myAbs(int n){
    return (n ^ n >> 31) - (n >> 31);
}
6.求平均值
int getAverage(int m,int n){
    return (m + n) >> 1;
}
  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值