一、位运算符
1. 位运算符简介
- 位运算符主要作用于位,是逐位进行操作。最常用的有:与 &、或 |、异或 ^。
- 常见的位运算符有(假设变量 A = 60,变量 B =13):
运算符 | 描述 | 实例 |
---|---|---|
& | 二进制与运算符:全 1 为 1 ,遇 0 则 0 | (A & B)= 12(0000 1100) |
| | 二进制或运算符:全 0 为 0,遇 1 则 1 | (A | B)= 61(0011 1101) |
^ | 二进制异或运算符:相同为 0 ,不同为 1 | (A ^ B)= 12(0011 0001) |
~ | 二进制补码运算符:0 变为 1 ,1 变为 0 | (~A)= -61(1100 0011) |
<< | 二进制左移运算符:左操作数的值向左移动右操作数指定的位数 | (A<<2)= 240(1111 0000) |
>> | 二进制右移运算符:左操作数的值向右移动右操作数指定的位数 | (A>>2)= 15(0000 1111) |
2. 为什么要使用位运算符
- C 语言位运算符在某些编程中如果灵活应用,则可以大大提高程序的执行效率,使程序执行时速度更高。
- C 语言主要应用于嵌入式开发、智能电器、通信行业等一些对效率和时间都要求很高的应用领域中,学好位运算符,在程序开发中灵活应用位运算符,往往能在这些应用中起到事半功倍的效果。
3. 与运算符
- 负数按补码形式参加按位与运算。
- 清零:如果想将一个单元清零,即使其全部二进制位为 0 ,只要与一个各位都为 0 的数值相与,结果为零。
- 取一个数的指定位:取数 X=1010 1110 的低4位,只需要另找一个数Y,令Y的低 4 位为1,其余位为0,即Y=0000 1111,然后将 X 与 Y 进行按位与运算即可得到 X 的指定位。
- 判断奇偶:根据最末位是 0 还是 1 来决定,为 0 就是偶数,为 1 就是奇数。因此可以用 if((a & 1) == 0)代替 if(a % 2 == 0)来判断a是不是偶数。
4. 或运算符
- 负数按补码形式参加按位与运算。
- 将一个数据的某些位设置为 1:取数 X=1010 1110 的低4位设置为1,只需要另找一个数 Y,令 Y 的低 4 位为 1 ,其余位为 0,即 Y=0000 1111,然后将 X 与 Y 进行按位或运算即可。
5. 异或运算符
- 翻转指定位:取数 X=1010 1110 的低 4 位进行翻转,只需要另找一个数 Y ,令 Y 的低 4 位为 1 ,其余位为 0 ,即 Y=0000 1111,然后将X与Y进行异或运算即可。
- 与 0 相异或值不变。
- 交换两个数:
void Swap(int &a, int &b)
{
if (a != b)
{
a ^= b;
b ^= a;
a ^= b;
}
}
6. 取反运算符
- 如对 a 按位取反,则得到的结果为 -(a+1) 。
- 对正数、负数和零都适用。
7. 左移运算符
- 左边的二进制位丢弃,右边补 0 。
- 若左移时舍弃的高位不包含 1,则每左移一位,相当于该数乘以 2。
8. 右移运算符
- 正数左补 0,负数左补 1,右边丢弃。
- 操作数每右移一位,相当于该数除以 2。
二、位运算例题——二进制中1的个数
题目描述
给定一个长度为 n 的数列,请你求出数列中每个数的二进制表示中 1 的个数。
输入格式
输入共两行。
第一行包含整数 n。
第二行包含 n 个整数,表示整个数列。
输出格式
共一行,包含 n 个整数,其中的第 i 个数表示数列中的第 i 个数的二进制表示中 1 的个数。
数据范围
1 ≤ n ≤ 100000
0 ≤ 数列中元素的值 ≤ 1e9
输入样例
5
1 2 3 4 5
输出样例
1 1 2 1 2
具体实现
1. 方法一(模板)
1.1 代码注解
- for(int i=x;i;i-= i&-i)
- i 表示 i 为真时继续执行。
- i-= i&-i 表示 i = i - (i&-i)。
- i & (-i) 表示的2^k,其中 k 表示 i 中从低位到高位的第一个 1 的位索引。
1.2 实现代码
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n;
cin>>n;
while(n--)
{
int x;
int res=0;
cin>>x;
for(int i=x;i;i-= i&-i)
{
res++ ;
}
cout<<res<<" ";
}
system("pause");
return 0;
}
2. 方法二( lowbit )
1.1 实现思路
- lowbit(x) 是 x 的二进制表达式中最低位的 1 所对应的值。
- 使用 lowbit 操作,每次 lowbit 操作截取一个数字最后一个 1 后面的所有位,每次减去 lowbit 得到的数字,直到数字减到 0 ,就得到了最终 1 的个数。
- 根据计算机负数表示的特点,如一个数字原码是 1000 1000 ,他的负数表示形式是补码,就是反码 +1 ,反码是 0111 0111 ,加一则是 0111 1000 ,二者按位与得到了1000 ,就是我们想要的 lowbit 操作。
1.2 实现代码
#include<bits/stdc++.h>
using namespace std;
int lowbit(int x)
{
return x&(-x);
}
int main()
{
int n;
cin>>n;
while(n--)
{
int x;
cin>>x;
int res=0;
while(x)
{
x-=lowbit(x);
res++;
}
cout<<res<<' ';
}
system("pause");
return 0;
}
3. 方法三(暴力枚举)
1.1 实现思路
- 对于每个数字a,a &1得到了该数字的最后一位,之后将a右移一位,直到位0,就得到了1的个数。
1.2 实现代码
#include <bits/stdc++.h>
using namespace std;
int n;
int a,k;
int main()
{
cin>>n;
for(int i=0;i<n;i++)
{
cin>>a;
k=0;
while(a)
{
k+=a&1;
a=a>>1;
}
cout<<k<<" ";
}
system("pause");
return 0;
}