关于位运算
a | b | & |
---|---|---|
0 | 0 | 0 |
0 | 1 | 0 |
1 | 0 | 0 |
1 | 1 | 1 |
a | b | or |
---|---|---|
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 1 |
a | b | ^ |
---|---|---|
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 0 |
8 byte区分“>>”和">>>":-5为10000101, -5 >> 1 答案为 11000010,但 -5>>>1 答案为01000010(使用符号位与0填充的区别)
关于左移:左移n位相当于原数乘上 2n ,比如 3 << 2 相当于 00000011 变成 00001100 = 12 = 3*22
关于右移:当操作数为正数时,右移与无符号右移作用一样,右移 n 位相当于原数除以 2n 取整
例题:
思路1:暴力破解,再开一个大小为1000的数组num,初始值为0,遍历arr数组,
num[arr[i]]++,当num[arr[i]]为2时直接输出arr[i];
#include
using namespace std;
int main() {
int arr[1001] = {
0};
for(int i = 0; i < 1000; i++)
arr[i] = i + 1;
arr[1000] = rand();
while(arr[1000] > 1000 || arr[1000] < 1)
arr[1000] = rand();
//printf("随机数是:%d\n",arr[1000]);
int num[1002] = {
0};
for(int i = 0; i <= 1000; i++) {
num[arr[i]]++;
if(num[arr[i]] == 2) {
printf("%d", arr[i]);
break;
}
}
return 0;
}
思路2:将arr数组所有元素异或(nor),再将1——1000所有的数异或(mo),将nor与mo异或,就可以直接得到k。
(异或可以去重)
#include
using namespace std;
int main() {
int arr[1001] = {
0};
int nor = 1;
for(int i = 0; i < 1000; i++)
arr[i] = i + 1;
arr[1000] = rand();
while(arr[1000] > 1000 || arr[1000] < 1)
arr[1000] = rand(); //这个while控制重复的k在1——1000之间
//printf("随机数是:%d\n", arr[1000]); //打印k
for(int i = 0; i <= 1000; i++)
nor ^= arr[i];
//printf("nor=%d\n", nor); //nor是arr数组的异或
int mo = 1;
for(int i = 1; i < 1001; i++)
mo ^= i;
//printf("mo=%d\n", mo); //mo是1——1000的异或
int ans = mo ^ nor; //nor和mo异或得答案
printf("%d", ans);
return 0;
}
思路一暴力解法更加好理解,但是多开了一个数组,但是当理解了异或位运算可以去重之后,思路二其实更加简单。
自我理解,异或可以去掉偶数个重复的数,剩下奇数个。
例题2:
给一个数,判断这个数的二进制中有几个1。
思路:该数为x,x与x-1的效果为去掉二进制码最后的那个1,x再等于x与x-1的结果。
例:x=9,
9的二进制为1001,8的二进制为1000,9&8=1000,此时x=1000=8
7的二进制为0111,8&7=0000
当x=0时终止循环。
代码:
#include
using namespace std;
int main() {
int x;
while(~scanf("%d", &x)) {
int ans = 0;
int y = x;
while(x != 0) {
x &= (x - 1);
ans++;
}
printf("%d的二进制中有%d个1\n", y, ans);
}
return 0;
}
改编:判断一个数是不是2的整数次方
思路:
一个数是2的整数次方的条件是它的二进制只有一个1,那么就在之前的代码上进行改动,当只有一个1时,这个数就是2的整数次方
代码:
#include
using namespace std;
int main() {
int x;
while(~scanf("%d", &x)) {
int ans = 0;
int y = x;
while(x != 0) {
x &= (x - 1);
ans++;
}
if(ans == 1)
printf("%d是2的整数次方\n", y);
else
printf("%d不是2的整数次方\n", y);
}
return 0;
}
例题3:给定一个介于0到1之间的浮点数,打印它的二进制,如果该数无法用32位以内的二进制精确表示,则打印“ERROR”
思路:小数转化为n进制,用该数乘n,得数的整数部位为小数的位数,小数部分仍然乘n
例:0.625转换:
1:0.625 * 2 =1.25
2:0.25 * 2=0.5
3:0.5 * 2=1.0
故0.625的二进制表示为0.101
代码:
#include
using namespace std;
int main() {
double x;
while(~scanf("%lf", &x)) {
int cnt = 0;
int byte[50]={
0};
bool flag = 0;
while(x != 0) {
if(cnt == 32) {
flag = 1;
break;
}
cnt++;
x *= 2;
if(x >= 1) {
byte[cnt] = 1;
x -= 1;
} else
byte[cnt] = 0;
}
if(flag)
printf("ERROR");
else {
printf("0.");
for(int i = 1; i <= cnt; i++)
printf("%d", byte[i]);
}
printf("\n");
}
return 0;
}
例题4:给一个数,将它的二进制奇偶位互换
思路:
将这个数与01010101010101010101010101010101做与运算,保留该数的奇数位;
再将原数与10101010101010101010101010101010做与运算,保留该数的偶数位;
再将偶数位右移一位,奇数位左移一位,做异或运算
最后就得到了奇偶位互换的答案
因为之前那两串32位二进制太长,所以用16进制表示位0x55555555和0xaaaaaaaa
代码:
#include
using namespace std;
int main() {
int x;
while(~scanf("%d", &x)) {
int odd = x & 0xaaaaaa;
int even = x & 0x555555;
int ans = (odd >> 1) ^ (even << 1);
printf("%d\n", ans);
}
}
——2019 / 5 / 9
之后待补充