任何信息在计算机中都是采用二进制表示的,数据在计算机中是以补码形式存储的,位运算就是直接对整数在内存中的二进制位进行运算。由于位运算直接对内存数据进行操作,不需要转换成十进制,因此处理速度非常快,在信息学竞赛中往往可以优化理论时间复杂度的系数。同时,一个整数的各个二进制位互不影响,利用位运算的一些技巧可以帮助我们简化程序代码。
C++提供了按位与(&)、按位或(I)、按位异或(^)、取反(~)、左移(<<)、右移(>>)这6种位运算符。这些运算符只能用于整型操作数,即只能用于带符号或无符号的char、short、int与long类型。
“a&b”是指将参加运算的两个整数a和b,按二进制位进行“与”运算。如果两个相应的二进制位数字都为1,则该位的结果为1;否则为0。这里的1可以理解为逻辑中的true,0可以理解为逻辑中的false。“按位与”其实与逻辑上“与”的运算规则一致。
//3-00000011
//5-00000101
int a = 3;
int b = 5;
int c = a & b;
cout << c<< endl; // 00000001
“a|b”是指将参加运算的两个整数a和b,按二进制位进行“或”运算。如果两个相应的二进制位数字有一个为1,则该位的结果为1否则为0。“按位或”其实与逻辑上“或”的运算规则一致。
// 48 - 00110000
//15 - 0000111
int a = 48;
int b = 15;
int c = a | b;
cout<< c<< endl; // 63 - 00111111
“a^b”是指将参加运算的两个整数a和b,按二进制位进行“异或”运算。如果两个相应的二进制位数字不相同,则该位的结果为1;否则为0。
a^0 = a a^a = 0
// 52 - 00110100
//15-00001111
int a = 52;
int b = 15;
int c = a ^ b;
cout << c<< endl; // 59 - 00111011
“~a”是指将整数a的各个二进制位都取反,即1变为0,0变为1。“~”是一元运算符。
cout << ~9 << endl;
/*
9 - 00001001
取反 - 11110110 补码
11110101 反码
10001010 原码
最高位 1 表示负数 -10
*/
“a<<b”是指将整数a的各个二进制位左移b位,高位丢弃,低位用0补齐。需要注意的是b必须是非负整数。在高位没有1丢弃的情况下,a<<1相当于a*2。
char a = (143<<2);
printf("%d", a);
/*
143 - 10001111
左移2 - 1000111100 60
*/
“a>>b”是指将整数a的各个二进制位右移b位,低位丢弃。对于无符号数,高位补0。对于有符号数,某些机器将对左边空出的部分用符号位填补(即“算术移位”),而另一些机器则对左边空出的部分用0填补(即“逻辑移位”)。同样,b必须是非负整数。a>>1相当于a/2。
char a = (15 >> 2 );
printf( "%d",a);
/*
15 - 00001111
右移位2 - 00000011 3
*/
char a = (-15 >> 2 );
printf( "%d",a );
/*
15 - 10001111
反码 - 11110000
补码 - 11110001
右移2 - 1111110001
反码 - 11111011
原码 - 10000100 -4
*/
位运算符也可以与赋值运算符组成复合运算符。
例如a &=b相当于a=a&b,a<<=2相当于a=a<<2。
位运算符也是有优先级的,例如
- “加减”高于“>>”和“<<”
- “~”与“!”、“++”、“-”一致 ,高于“乘除”
- “关系运算” 高于 “&” 高于 “^” 高于 “|”高于“&&”和“||”
为了避免出错,增强程序的可读性,利于调试,建议在需要的地方添加括号来保证优先运算。比如1<<5-1,建议写成1<<(5-1),等价于1<<4。
例1:交换和取平均值
#include<iostream>
using namespace std;
int main()
{ //交换
int x = 3,y = 9,z;
x^= y; //x = x^y;
y^= x; // y = y^x = (y ^ (x^y) ) =x;
x^= y; //x = x^y = (x^y)^x = y;
cout << x << " " << y << endl;
//取平均值
z = (x&y) + ((x^y)>>1 );
cout << z << endl;
return 0;
}
例2:整数幂
判断一个数n是不是2的整数幂,比如64=2^6,所以输出“yes”,而65 无法表示成2的整数幂形式,所以输出"no"。n在int 范围以内。
- 【问题分析】
- 我们考虑一个数如果是2的整数幂会有什么特殊性。观察发现64转换成二进制为0100000,只有一个位是1。将这个数减去1,就变成00111111的形式,我们将这2个数做按位与运算,发现结果为0。分析发现,如果一个数不能表示成2的整数幂形式,则以上过程的运算结果一定不为0。所以,可以利用位运算将算法的时间复杂度优化成O(1)。
#include<iostream>
using namespace std;
int main()
{
int n;
cin >> n;
if (n&(n-1))
cout << "no";
else
cout << "yes";
return 0;
}