首页 > 代码库 > 位操作 注意事项

位操作 注意事项

逐步更新。。。

1.位操作符的运算优先级比较低,因此尽量使用括号来确保运算顺序,否则很可能会得到莫明其妙的结果。比如要得到像1,3,5,9这些2^i+1的数字。写成int a = 1 << i + 1;是不对的,程序会先执行i + 1,再执行左移操作。应该写成int a = (1 << i) + 1;

2.奇偶判断

for (i = 0; i < 100; ++i)//输出0~100内所有奇数
	if (i & 1)
	    printf("%d ", i);
putchar('\n');

3.不用第三方变量交换两数

void Swap(int &a, int &b)
{
if (a != b)//这里是必须的,不然就会a^b==0,结果就是本来a、b用来交换的,经此函数操作后,都变成0了
{
a ^= b;
b ^= a;
a ^= b;
}
}

4.变换符号

取反加一

int sign_rev(int a){
    return ~a+1;
}

5.求绝对值

int my_abs(int a)
{
    int i=a>>31;//若a为正数,则i为0,若a为负数,则i为-1,0异或某数为某数,-1异或某数,等于某数取反
    return (i^a)-i;
}

6.二进制逆序

第一步:每2位为一组,组内高低位交换

      10 00 01 10  11 01 10 00

  -->01 00 10 01 11 10 01 00

第二步:每4位为一组,组内高低位交换

      0100 1001 1110 0100

  -->0001 0110 1011 0001

第三步:每8位为一组,组内高低位交换

      00010110 10110001

  -->01100001 00011011

第四步:每16位为一组,组内高低位交换

      01100001 00011011

  -->00011011 01100001

#include <stdio.h>
void print(int a)
{
    int i;
    for(i=sizeof(a)*8-1;i>=0;--i)
    {
        if((a>>i)&1)
            putchar('1');
        else
            putchar('0');
        if(i%8==0)
            putchar(' ');
    }
    putchar('\n');
}
int main(int argc, char *argv[])
{
    unsigned int a=34520;
    print(a);
    a=((a&0xaaaaaaaa)>>1)|((a&0x55555555)<<1);
    print(a);
    a = ((a & 0xCCCCCCCC) >> 2) | ((a & 0x33333333) << 2);
    print(a);
    a = ((a & 0xF0F0F0F0) >> 4) | ((a & 0x0F0F0F0F) << 4);
    print(a);
    a = ((a & 0xFF00FF00) >> 8) | ((a & 0x00FF00FF) << 8);
    print(a);
    unsigned int b=0;
    for(short int i=1;i!=0;i<<=1)
    {
        b<<=1;
        if(a&1)
            b|=1;
        a>>=1;
    }
    print(b);
    return 0;
}

7.二进制中1的个数

1)先写个衍生于树状数组中的做法:

int cnt=0;
    while(b){
        b-=(b&-b);
        cnt++;
    }
printf("cnt is %d\n",cnt);
不断减去二进制最后一个1,最后就能到一共减了几次,也就是有多少个1。其运算次数与输入n的大小无关,只与n中1的个数有关。如果n的二进制表示中有k个1,那么这个方法只需要循环k次即可

2)

速度不一定最快,但是想法绝对巧妙。 说一下其中奥妙,其实很简单,先将n写成二进制形式,然后相邻位相加,重复这个过程,直到只剩下一位。以217(11011001)为例,有图有真相,下面的图足以说明一切了。217的二进制表示中有5个1:


int BitCount4(unsigned int n) 
{ 
    n = (n &0x55555555) + ((n >>1) &0x55555555) ; 
    n = (n &0x33333333) + ((n >>2) &0x33333333) ; 
    n = (n &0x0f0f0f0f) + ((n >>4) &0x0f0f0f0f) ; 
    n = (n &0x00ff00ff) + ((n >>8) &0x00ff00ff) ; 
    n = (n &0x0000ffff) + ((n >>16) &0x0000ffff) ; 

    return n ; 
}
8. 缺失的数字 LeetCode的那个single number

必定有一个数只出现一次而且其它数字都出现了偶数次。用搜索来做就没必要了,利用异或运算的两个特性——1.自己与自己异或结果为0,2.异或满足交换律。因此我们将这些数字全异或一遍,结果就一定是那个仅出现一个的那个数

const int MAXN = 15;
int data[MAXN] = {1, 347, 6, 9, 13, 65, 889, 712, 889, 347, 1, 9, 65, 13, 712};
int lostNum = 0;
for (int i = 0; i < MAXN; i++)
	lostNum ^= data[i];
printf("缺失的数字为:  %d\n", lostNum);




位操作 注意事项