位运算

位运算

位运算是将整数或字符转化为2进制,对2进制的每一位进行运算。

由于计算机在进行十进制运算时,都要将十进制转化为二进制再进行计算。那么如果一开始就用二进制计算也就是位运算,那么效率将会比十进制转化为二进制再进行运算要高得多,也就是说,这是一个十进制运算的底层算法,省去了将十进制转化为二进制的时间,以提高效率。

移位

左移

x<<n;  //这表示将操作数x按二进制向左移动n位,右边新生成的的空位补0

类比十进制乘法.事实上,这相当于

但是这其中会遇到溢出的问题(移动的位数过多),但是Python不会(Python是最好的编程语言!!!),因为Python会在溢出后自动选择更大的数据类型。

右移

算数右移(仅有Java支持)

x>>n; //将操作数x按照按进制向右移动n位,左边按照原来的最高位来补   

事实上,类比十进制除法和左移。这相当于

由于前面按照最高位补,则不会改变原来数字的符号。这保证了除法的可行。这引出了下面的另一种右移:无符号右移。

逻辑右移(C/C++对于无符号数均为逻辑右移,对于有符号数取决于编译器)

x>>>n; //将操作数x按二进制向右移动n位,左边补0

移动过后都变成正数了。

按位与(&)

相当于逻辑且,例如:3&6

用途

清零(把开关弹起来)

只要符合原数的1位对应为0,进行与操作后就可以全部变为0。当然直接用全为0的数进行与运算更容易。例如:

取出特定位数的数

由于按位与如果是1&1=1,如果是0&1=0,这相当于如果用1来与(&)原数对应的位,那么对于不改变原数;用0与,则不管是什么都会变成0。也就是说,如果我们有两个数(以整数为例)a,b.想要取出a中特定位数的数,使用b作为算子。那么我们只需要将b在我们需要取出的数位赋为1,其余均为0即可,例如:

如果我们想要取出从左到右的第、、、位,我们就可以这样设置那么进行按位与操作后就可以得到:的第、、、位就被我们取出来赋值给了

代码实现:

int GetBit(int a, int i)
{
  //这个GetBit函数用来返回a的二进制表示中的从右向左第i+1位的二进制数。这只需要用到与运算
  //由于i<<i扩大了2^i倍,最后还要再除以2^i
  return ((1<<i) &a)>>i ;
}

上述是使i的第i+1位变成1去与a,也可以反过来让a把最后一位变成第i+1位的二进制数与1


int GetBit(int a,int i)
{
  return (a>>i)&1;
}

按位或(|)

相当于逻辑或,例如3|6

应用

设置某位为1(把开关按下去)

由于1|1=1,1|0=1,0|0=0.所以一个数|1,那么结果一定是1;一个数|0,则不改变原数.利用这一特点,可以设计使用按位或对于某数的特定位数设置为1,要达到这一目标,只要将我们想要设置为1的数位设为1,以构造b即可.

例如:

想要让的第、位设置为,那么我们可以这样来设计那么我们可以通过得到

代码实现:


int SetBit(int a,int i,int j)
{
  if(j==1)  //将a的从右至左第i+1位设置为1.
    return a|(1<<i);
  else if(j==0)  //将a的从右至左第i+1位设置为0.
    return ~a&(1<<i);
  return -1;
}

按位非(~)

略。本质是反码。

注意:对于符号位同样也会取反

代码实现:

int Flip(int a)
{
  //想要反转,只要使用非运算即可.
  return ~a;
}

按位异或(^)

相同为0,不同为1,等价于不进位的加法,可以区分两个数字是否相同。例如:3^6

应用

反转特定位(按开关)——强化版的非运算

如果想要将1变为0,那么只要使两个数字相同,也就是用1^1=1;想要将0变为1,只要使两个数字不同,也就是0^1=1。那么只要用1异或一个数,无论如何都可以起到反转这个数位的作用

例如:

想要将后四位反转,达到的效果那么只需要这样来设计那么就得到这样就在前四位不变的情况下,将后四位反转了

进一步地,我们可以知道非运算等价于a与一个 每个数位都是1 的数进行异或

交换两数

a=a^b;
b=b^a;
a=a^b;  //这样就可以实现不用中间变量实现交换两数。然而这样比较复杂。且不容易debug,不容易让别的程序员看懂以至于容易挨揍。

这相当于


a=(a^b)^(b^(a^b))=a^b^b^a^b=a^a^b^b^b=b//这里有一个疑问:为什么或运算满足交换律?
消去二进制中最右侧的1

可以用x&(x-1)来消去x的最后一位的1.这是因为(x-1)如果转化为二进制就相当于将x的最右边一个1变为0

例如:

这个应用可以找到二进制表达式中最右侧的1并消去.

不同长度的数据进行位运算

按照较长数据的长度,将较短数据“右移”至与较长数据长度相同。其实本质上还是在不改变符号的前提下扩展数据

复合的位运算应用

位运算的公式们

-x = ~x + 1 = ~(x-1)

~x = -x-1

-(~x) = x+1

~(-x) = x-1

x+y = x - ~y - 1 = (x|y)+(x&y)

x-y = x + ~y + 1 = (x|~y)-(~x&y)

x^y = (x|y)-(x&y)

x|y = (x&~y)+y

x&y = (~x|y)-~x

x==y: ~(x-y|y-x)

x!=y: x-y|y-x

x< y: (x-y)^((x^y)&((x-y)^x))

x<=y: (x|~y)&((x^y)|~(y-x))

x< y: (~x&y)|((~x|y)&(x-y))//无符号x,y比较

x<=y: (~x|y)&((x^y)|~(y-x))//无符号x,y比较

给出两个32位的整数N和M,以及两个二进制位的位置i和j。写一个方法来使得N中的第i到j位等于M(M会是N中从第i为开始到第j位的子串)

这是一道不值一提的题目,只不过是前面两种技巧的复合而已。略。

给出两个整数a和b, 求他们的和, 但不能使用 + 等数学运算符。

这道题是一道好题,与数学结合得比较紧密。我们知道异或(^)运算是不进位加法,现在要考虑进位加法,该怎么办?

首先考虑如何判断哪一位需要进位?在二进制中,如果两个数的同一位都是1,那么就需要进位了,例如:

那么a+b的第三位就需要进位。那么 如何判断是否进位 这个问题就转换为了 如何判断两个数的同一位都是1?显然,这就是与运算的定义。

我们判断了是否需要进位,接下来,我们该怎么表示进位呢?进位,实际上就是将这一位变为0,然后将下一位变为1。颇有一点乘法的味道,没错,这时需要用到左移运算(乘法)。即当a&b=1时,将a&b左移一位(1×2)。当a&b=0时,不左移。事实上,不左移与0左移1位(0×2=0)是一样的,那么上述分类讨论就可以合并为:


(a&b)<<1 //表示进位

表示进位后,我们就可以将进位与不进位结合起来,得到最终的结果:


a+b=(a^b)+(a&b<<1) //对于每一位来讲的最终结果

可以使用循环来对每一位都进行上述操作,最终就可以解决了。这里直接贴一段九章算法网站的Java代码:


class Solution {
    /*
     * param a: The first integer
     * param b: The second integer
     * return: The sum of a and b
     */
    public int aplusb(int a, int b) {
        while (b != 0) {
            int _a = a ^ b;
            int _b = (a & b) << 1;
            a = _a;
            b = _b;
        }
        return a;
    }
};

用O(1)时间检测整数是否是2的正整数次幂

则N必满足:

1.N>0

2.N的二进制表示中只有一个1,这是因为其一定可以写为1(其中这个1位于右向左第n+1位)后面全是0的形式(由于2进制的定义)

那么如果将N的二进制表达中唯一的1消去,应该返回0.利用这一特点我们立刻可以写出时间复杂度位O(1)的代码,还是直接粘贴了……


class Solution {
public:
    /*
     * @param n: An integer
     * @return: True or false
     */
    bool checkPowerOf2(int n) {
        // write your code here
        return n > 0 && (n & (n - 1)) == 0;
    }
};

统计二进制表达式中有多少个1

这是一道不足挂齿的题目,略。

如果将整数A变为B,需要改变多少个bit位?

只需要考虑A与B有多少位不同即可。只要使用异或(相同为0,不同为1)即可,略。

枚举子集

由于二进制只具有两种状态,这个"解空间"与布尔型的"取或不取"的"解空间"同构。那么我们可以用二进制的第i位表示有序集的第i位取出或者不取出

题目待补充。

找出只出现奇数次的数

由于a^b^b=a,那么我们只要将所有的数异或两次,找变化的即可,略。

数组中,只有一个数出现一次,剩下都出现三次,找出出现一次的数

待补充。

参考资料

1.黄程程讲编程 Java017 位运算:移位、按位与、或、非、异或

2.位运算

3.九章算法-位运算

4.位运算口诀与应用实例

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值