位运算中的技巧

上一篇文章说说二进制中我们说了说位运算的基础知识,其中提到了位运算能够实现一些巧妙的算法,今天就来看看有那些技巧吧。

目录

一、基础技能

按位与技巧

1.判断奇偶数

2.判断是否为2的次幂

3.判断某数的二进制中数字1的个数

按位或技巧

1.与空格或运算将英文字符转换为小写字符

按位异或技巧

1.交换两个数

2.找出没有重复的那一个数字

二、进阶组合技

1.汉明距离

2.列举一个数组的各个子集


一、基础技能

按位与技巧

1.判断奇偶数

i&1=0   偶数

i&1=1   奇数

根据偶数的二进制形式我们不难发现最低位(最右侧)的位置代表2的0次方,即是1。那么凡是奇数其最低位必定是1,类比可得凡是偶数则必然最低位为0。因而我们只需将所要查验的数与‘1’进行与运算即可。

2.判断是否为2的次幂

我们知道凡是偶数则最低位为零,那么既然是二进制形式,与‘2’是不是也有什么特别的关系呢?

我们知道,二进制中从最低位开始每一位都是2的次幂,也就意味着凡是2的次幂,其后必然是0。那么2的次幂减一的二进制表示就是一堆1。

2    0000 0010               1    0000 0001

4    0000 0100               3    0000 0011

8    0000 1000               7    0000 0111

16   0001 0000               15   0000 1111

32   0010 0000               31   0001 1111

64   0100 0000               63   0011 1111

128  1000 0000               127  0111 1111

由上述的规律和与运算的特性可知,判断整数n是否为2的次幂的条件如下

n>0;

n&(n-1)=0

3.判断某数的二进制中数字1的个数

由上述我们可以发现,当n是2的次幂时,n&(n-1)=0,似乎把n的二进制形式中的1消成了0。那这个特性对其他数字是否适用呢?

X=27           0001 1011

X=26      &   0001 1010

                00011010

由此可见也是可以的,我们只需要记录其循环的次数即可,主体代码如下:

int coun=0;   //计数变量

while(n){

n=n&(n-1);   //进行与运算

coun++;  }

按位或技巧

1.与空格或运算将英文字符转换为小写字符

根据英文字母的ASCII码之间的关系我们知道,‘a'比’A‘的ASCII码大32,而‘ ’的ASCII码恰好是32,那这三者中间是不是有什么关系呢?(该部分与位运算的应用较多,在这里不在赘述,有兴趣的话可以自己动脑多思考思考哦!)

'a' | ' ' = 'a' 

     1100001    ‘a’=97

|    0100000    ‘ ’=32

------------------------------

      1100001       97 ->'a'

'A'|' '='a' 

      1000001     ‘A’=65

|      0100000      ‘ ‘=32

------------------------------               实现'A'-->'a'

       1100001      97=’a’  

按位异或技巧

首先我们来看看异或运算的一些特殊规律

1.n^n=0      n^0=n

13 ^ 13                               13^0

     0111                                 0111

^   0111                          ^     0000

    0000   ->0                         0111  ->13

2.(a^b)^c = a^ (b ^c)

(13^5)^6=14                                   13^(5^6)=14

   13      1101                                   5     0101

^    5      0101                              ^   6     0110

   (8)      1000                                  (3)     0011

^    6      0110                              ^  13      1101

    14      1110                                  14       1110

知道了以上的规律,接下来就是介绍技巧的时候了

1.交换两个数

问题:已知a、b两个数字的值,请交换a、b的值

在刚开始的时候我们通常会引用第三个变量来进行交换。但当我们知道了位运算以后可以借助异或运算来进行交换,代码如下:

(1)a = a ^ b;

(2)b = a ^ b;

(3)a = a ^ b;

有木有被惊讶到呢?下面就来解释一下是如何交换的:

  • 将a赋值为a ^ b;
  • 令b = a^b,请注意,此时的a不再是a而是a ^ b。展开来看第二步就是b=a^b^b;由异或运算第一条规律可知,b^b=0,a^0=a,因而第二步简化过后就是b=a
  • 规律同②,将(1)(2)代入(3)a=(a^b)^(a^b^b),由异或运算的第二条规律可得,a=(a^a)^(b^b)^b=0^0^b=b,即a=b,交换完成

2.找出没有重复的那一个数字

问题:已知一个大小为n的数组中,只有一个数字出现了一次,其他的数字均出现两次,请找出这个只出现了一次的数字。

根据异或运算的规律我们可以知道,当我们把数组中所有的数字都进行异或运算,那么凡是两个相同的数据都会异或为‘0’,而只出现一次的那个数字则会与‘0’异或,最后又得到了它本身。主体代码如下(以int类型作为示例):

int sum=0,i;

if(i=0;i<n;i++)

sum=sum^arr[i];

printf(“%d”,sum);

二、进阶组合技

1.汉明距离

问题:两个整数之间的 汉明距离 指的是这两个数字对应二进制位不同的位置的数目。给你两个整数 x 和 y,计算并返回它们之间的汉明距离。

我们知道异或中,对应位上两数不同则为‘1’,而在与运算中我们知道了如何计算一个二进制数中‘1’的个数,那如果把这两个运算结合起来就可以解决汉明距离这个问题啦。主体代码如下:

int c=x^y;  //用异或运算找到两个数中的不同位置
int coun=0;  //计数变量
while(c){
c=c&(c-1);   //统计异或后数字中‘1’的个数
coun++;
}
printf("%d",coun);

2.列举一个数组的各个子集

根据数学公式我们知道,一个有n的元素的数集,它有2的n次方个子集,有2的n-1次方个非空子集。同理,数组也有类似的性质,例如:

数组arr[3]={3,6,9}

那么它的子集是{}、{3}、{6}、{9}、{3,6}、{3,9}、{6,9}、{3,6,9}

当与位运算结合时,我们可以发现正整数二进制可以与原始数组相对应,用第i位是1还是0代表这个数取或不取,从而得到不同的子集:

arr[3]     =     {3,6,9}

 {}                   000

{3}                  100

{6}                  010

{9}                  001

{3,6}            110

{3,9}            101

{6,9}             011

{3,6,9}       111

问题:(力扣  2044. 统计按位或能得到最大值的子集数目

给你一个整数数组 nums ,请你找出 nums 子集 按位或 可能得到的 最大值 ,并返回按位或能得到最大值的 不同非空子集的数目 。

如果数组 a 可以由数组 b 删除一些元素(或不删除)得到,则认为数组 a 是数组 b 的一个 子集 。如果选中的元素下标位置不一样,则认为两个子集 不同 。

对数组 a 执行 按位或 ,结果等于 a[0] OR a[1] OR ... OR a[a.length - 1](下标从 0 开始)。

int countMaxOrSubsets(int* nums, int numsSize){
    int i,j,sum,cnt=0;
    int max=0;
    for(i=0;i<(1<<numsSize);i++){   //i<(1<<numsSize)==2的numsSize次方,遍历所有子集的个数
      sum=0;
    for(j=0;j<numsSize;j++){     //遍历numsSize长度的二进制数
       if(i&(1<<j))              //检查该位上是否为‘1’,是否取了原数组的该位数   
       sum=sum | nums[j];}       //进行子集的或
       if(max<sum)
       {max=sum;
       cnt=1;}
       else if(max==sum)
       cnt++;
       }
       return cnt;       // 返回计数结果
}

以上就是我在学习位运算中学到的一点技巧。如有不对的地方还请指正,欢迎在评论区中一起探讨,共同进步!

位运算使用的是我们并不常用的二进制,因此在一些时候比较晦涩难懂,让人摸不到头脑。所以,以上的思路和代码要自己好好想想,并动手实践。勤练多思才能掌握。

整理不易,看到这就点个赞再走吧^-^

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值