算法:位运算常用的功能和操作总结

a乘以2除以2:a<<1   a >>1

判断奇偶性:a&1,为零为偶,否则奇数

交换两个数:a^=b^=a^=b;(可以不借助别的变量)

判断两个数是否相同:a^b==0则相同,否则不同(可以快速从只有一个数出现一次其余都出现偶数次的一串数里找到这个数,

1 1 2 3 3 3 3 4 4 ,从头异或一遍,就可以得到2)其实异或还有更大作用的用法,下面会提到

去掉二进制最后一个1:i-=i&-i,也可以写作i=i&(i-1),在树状数组里会用到

 

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

(1) 判断int型变量a是奇数还是偶数           

       a&1   = 0 偶数

       a&1 =   1 奇数

(2) 取int型变量a的第k位 (k=0,1,2……sizeof(int)),即a>>k&1

(3) 将int型变量a的第k位清0,即a=a&~(1<<k)

(4) 将int型变量a的第k位置1,即a=a|(1<<k)

(5) int型变量循环左移k次,即a=a<<k|a>>16-k   (设sizeof(int)=16)

(6) int型变量a循环右移k次,即a=a>>k|a<<16-k   (设sizeof(int)=16)

(7)整数的平均值

对于两个整数x,y,如果用 (x+y)/2 求平均值,会产生溢出,因为 x+y 可能会大于INT_MAX,但是我们知道它们的平均值是肯定不会溢出的,我们用如下算法:

int average(int x, int y)   //返回X,Y 的平均值

{   

     return (x&y)+((x^y)>>1);

}

(8)判断一个整数是不是2的幂,对于一个数 x >= 0,判断他是不是2的幂

boolean power2(int x)

{

    return ((x&(x-1))==0)&&(x!=0);

}

(9)不用temp交换两个整数

void swap(int x , int y)

{

    x ^= y;

    y ^= x;

    x ^= y;

}

================================================================================================================================================================================================

1. 0110+0110 
两个相同的数相加,相当于0110*2, 所以我们把0110左移一位就可以了(0110<<1),结果是1100。 

2. 0100*0011 
因为0100相当于4,相当于2的平方,所以我们把0011左移两位就可以了,结果是1100。 

3. 1101^(~1101) 
如果我们按位运算它结果是1111,我们可以发现任何一个数异或它本身的非时(a^(~a)),结果是一个1s(1s代表一个1的序列,下面都用1s表示)。 

4. 1011&(~0<<2) 
这个例子的结果是1000。通过这个例子我们可以总结出一个数x和(~0 << n)进行按位与操作,x的最右边的n为将变为0的序列,换句话说我们清除了x最右边的n位。 

通过上面简单的例子我们得到下面的一些基本用法 (0s代表一个全为0的序列): 
1.  ^ 异或 

x ^ 0s = x 
x ^ 1s = ~x 
x ^ x = 0 

2. & 位与 

x & 0s = 0; 
x & 1s = x; 
x & x = x 

3. | 按位或 

x | 0s = x 
x | 1s = 1s; 
x | x = x; 

4. x & (1 << n) (得到特定位) 
1左移n位后我们得到一个类似于00001000的序列,所以通过这个例子我们把x所有位置0除了第n位,从而我们得到了第n位。 

5. x | (1 << n) (设置特定位) 
同第四个例子,1左移n位后我们得到一个类似于00001000的序列,x按位或00001000后我们能改变第n位,把她设置为1,x中其它位不会受到影响。 

6. x & (~ (1 << n)) (清除特定位) 
1左移n位后我们得到一个类似于00001000的序列,然后取非,得到11110111,x按位与11110111后我们只把第n位置0,其它位不会改变。类似的还有x &( (x << n) - 1) 和x &(~(-1 >>>(31 - i))) 

7. (x & (~(1 << n))) | (y << n) (把x的第n位设置成y) 
首先我们把x的第n位设置成0,然后把y左移n位得到一个第i位为y,其余位为0的序列,把这两个数位或就把x的第n位设置成了y。 

很多复杂的问题都可以通过这些基本的操作解决。在这里仅仅举几个简单的例子,对于很多题目都可以用位运算解决,希望大家善于思索, 感受位运算的魅力。 
1. 给定两个整数 a, b, 如果不用第三个变量, 怎样将两个数的数值交换? 
当然我们可以用其他方法解决,但如果我们在面试中用位运算来解决一个问题,也许会让面试官眼前一亮,这道题我们可以用异或来解决。 
首先我们令a = a ^ b; 然后b = a ^ b, 相当与 b = a ^ b ^ b = a ^ 0 = a; 最后我们令 a = a ^ b, 此时b = a, 也就是 a = a ^ b ^ a = b。从而完成了两个值的交换。 

2. 给定两个整数A, B,写一个方法确定需要几次换位才可以将A变为B。 
比如 A: 11010  B: 01100  输出:3 

我们首先要知道那些位需要替换,我们很容易想到异或这个功能,A^B,相同的位异或为0,不同的位异或为1,所以我们只需要确定异或之后的数有几个1即可。我们怎么确定呢?上面讲到了如何清除特定位,这里是个特例,例如A^B = x, 我们让x&(x-1),这个操作清除了x最右边的1,我们通过一个循环确定次数,直到x为0结束。代码如下: 

public int bitSwapNums(int a, int b) {  

        int count = 0;  

        for(int i = a ^ b; i > 0; i= i & (i - 1)){  

            count ++;  
        }  
        return count;  
} 

位运算的应用很灵活,我们也可以这样实现 

public int bitSwapNums(int a, int b) {  
                int count = 0;  
        for(int i = a ^ b; i > 0; i = i >> 1) {  
            count += i & 1;  
        }  
        return count;  
}  



每次循环i右移一位,通过与1进行位与得到1的个数。 


2.1 交换两个数

 int x = 1, y = 2;
  x ^= y;
  y ^= x;
  x ^= y;


2.2 判断一个数是不是2的幂次 
判断一个数是不是2的幂次,要分析2的次幂的数的二进制表示中,必然只有一个1,其余的都是0,要记住这个性质。而且n&(n-1)这也是一个重要的性质,可以实现把n的最右边的1变成0。

public static boolean mici(int a) {
        if(a==0) return false;
        return (a&(a-1))==0;
    }



2.3 求一个数的绝对值 
对于负数可以采用通过对其取反后加1来得到正数。首先是向右移动31位,如果是正数就会得到0,如果是负数就是得到-1,对于正数可以直接输出,对于负数可以取反加1。

int i=-1;
int j=i>>31;
 return  (i^j)-j;   

                           

2.4 求一个数的相反数 
可以使用取反操作,但需要注意的是取反之后要加1。

  int a=-1;                              
   int b=~a+1;  

                         

2.5 判断一个数的奇偶性 
只需要和1进行与操作即可,因为一个奇数的二进制表示中1的个数肯定是偶数,而一个偶数的的二进制表示中1的个数肯定是奇数个。

int a=9;
  return (a&1)==1;  

                             

2.6 、实现加法运算 
异或操作可以实现无进位的加法,要实现进位操作的话,需要有一个进位变量记录是否进位。

public static int getSum(int a, int b) {
        while (b != 0) {
            int c = a ^ b; //无进位加法
            b = (a & b) << 1;
            a = c;
        }
        return a;
    }



2.7 实现减法

    //实现减法 a-b
 

   public static int jianfa(int a,int b) {
        return getSum(a, ~b+1);
    }



2.7、实现正数的乘法

原理上还是通过加法计算。将b个a相加。

public static int Multi(int a, int b) {
        int ans = 0;
        while (b > 0) {
            if ((b & 1) != 0) {
                ans = getSum(ans, a);//两个数相加
            }
            a = a << 1;
            b = b >> 1;
        }
        return ans;
    }


2.8、实现正数除法 
本质上还是进行的减法操作,看看,除数可以减去多少次被除数。

    //实现正数除法
   

 public static int  divide(int a,int b) {
        int res=0;
        while(a>=b){
            a=jianfa(a,b);
            res++;
        }
        return res;
    }


2.9、统计一个数的二进制表示中有多少个1 
使用的性质就是一个数和这个数减去1相与的话实现的效果就是把二进制表示中的最后一个1变为0。这样就可以统计次数了,也就是1的个数。

public int hammingWeight(int n) {
        int sum=0;
        while(n!=0){
            sum++;
            n=n&(n-1);
        }
        return sum;
    }



2.10、查找序列中只出现一次的数,其余的数出现了2次 
一个数和自己异或操作就是0,这样就可以找出来,需要知道性质就是a^b^c=a^c^b

public int singleNumber(int[] nums) {
        int r=0;
       for(int i=0;i<nums.length;i++){
           r=r^nums[i];
       }
        return r;
    }


2.11、反转二进制序列,然后输出整数 
原数不断右移一位,然后取出最低位赋给新数的最低位,然后新数左移移位。

private static int rever(int a) {
        int res=0;
        for (int i = 0; i < 32; i++) {
            res=res<<1|(a&1);
            a>>=1;
        }
        return res;
    }



2.12、实现两个数的平均数

 int a=1;
  int b=9;
 return (a+b)>>1;

 

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值