目录
位运算和进制基础
位运算符
&(与),|(或),^(异或,~(非/取反)
&(与):运算规则:0&0=0; 0&1=0; 1&0=0; 1&1=1,说白了就是全为1&才为1,有一个是0结果就是0
举个例子:4&5 0000 0100&0000 0101=0000 0 100 所以4&5=4
|(或):运算规则:0|0=0; 0|1=1; 1|0=1; 1|1=1说白了就是 有一个是1结果就是1,全是0结果才为0
举个例子:4|5 0000 0100|0000 0101=0000 0101 4|5=5
^(异或):运算规则:0^0=0; 0^1=1; 1^0=1; 1^1=0;就是如果相同为0,不同为1
~(取反):运算规则:~1=0; ~0=1;
再说一说原码,反码和补码(最终在内存中以补码存放)
一个字节有8个bit位,计算机用最高位是符号位,正数为0,负数为1
一个 byte 有 8bit,最大值是 0 1 1 1 1 1 1 1 (+127),最小值是 1 1 1 1 1 1 1 1 (-128)
十进制数据的二进制表现形式就是原码,原码最左边的一个数字就是符号位,0为正,1为负。
举个例子:15的原码是0000 1111
负数反码是原码符号位不变,其余位取反得到的,补码 = 反码+1
需要注意的是正数的原码=反码=补码,负数的反码 = 原码的符号位不变其余按位取反,负数的补码 = 反码+1
一个整型类型是4个字节,一个字节8个bit,所以一个整型类型32位
我们举出一个原码,反码,补码的例子
15
原码:0000 0000 0000 0000 0000 0000 0000 1111
反码:0000 0000 0000 0000 0000 0000 0000 1111
补码:0000 0000 0000 0000 0000 0000 0000 1111
-15
原码:1000 0000 0000 0000 0000 0000 0000 1111
反码:1111 1111 1111 1111 1111 1111 1111 0000(第一位的1不变,其余按位取反)
补码:1111 1111 1111 1111 1111 1111 1111 0001(反码+1)
<<和>>和>>>
<<是将二进制向左移动,补充0
>>是将二进制向右移动,补充最高位
>>>将二进制向右移动,补充0
15<<1
0000 0000 0000 0000 0000 0000 0000 1111
0000 0000 0000 0000 0000 0000 0001 1110
-15<<1(符号位不变)
1000 0000 0000 0000 0000 0000 0000 1111
1000 0000 0000 0000 0000 0000 0001 1110
15>>1
0000 0000 0000 0000 0000 0000 0000 1111
0000 0000 0000 0000 0000 0000 0000 0111(补充符号位)
-15>>1
1000 0000 0000 0000 0000 0000 0000 1111
1100 0000 0000 0000 0000 0000 0000 0111
位运算在算法中的应用
判断奇数偶数
#include<iostream>
using namespace std;
int main()
{
int a = 10;
int b = a & 1;
cout << "b: " << b << endl;
int c = 15;
int d = c & 1;
cout << "d: " << d << endl;
return 0;
}
我们可以让值&1,如果为0就是偶数,如果为1就是奇数
获取指定二进制位是1还是0
#include<iostream>
using namespace std;
int main()
{
int c = 15;
//问第4位上是1还是0
int d = 1;
d << 3;
d = d & c;
d >> 3;
cout << d<<endl;
return 0;
}
我们让1先移动到指定位置,然后和我们给的值按位与,在让按位与后的值移动到第一位,打印输出,如果为0原来位上是0,结果为1原来位上为1
交换两个整数变量的值
#include<iostream>
using namespace std;
int main()
{
int a = 10;
int b = 5;
a = a ^ b;
b = a ^ b;
a = a ^ b;
cout << a << " " << b << endl;
return 0;
}
三次按位异或就行了
例题
1.如何找出数组中唯一成对的那个数(不开辅助空间)
#include<iostream>
using namespace std;
int main()
{
int n;
cin >> n;
int* a = (int*)malloc(sizeof(int)*(n+1));
for (int i = 0; i < n; i++)
{
a[i] = i+1;
}
int index = rand() % (n - 0);
a[n] = a[index];
for (int i = 0; i <= n; i++)
{
cout << a[i]<<" ";
}
cout << endl;
int x1 = 0;
for (int i = 1; i <= n; i++)
{
x1 = x1 ^ i;
}
for (int i = 0; i <= n; i++)
{
x1 = x1 ^ a[i];
}
cout << x1;
return 0;
}
思路就是我让连续的数去连续按位异或,1^2^3^4...^n,我先开一个x1=0,0异或其他任何数都是其他数据,异或完之后,x1再和有重复数据的数组去连续异或,如果一个数出现偶数次就会被消掉,奇数次就会被保留,因为重复数组中那个重复的数K出现了2次,但是我们先异或了一下连续数组,这个K就相当于异或了3次,是奇数次就会保留了下来。
写一个开辅助空间的
#include<iostream>
using namespace std;
int main()
{
int n;
cin >> n;
int* a = (int*)malloc(sizeof(int) * (n + 1));
for (int i = 0; i < n; i++)
{
a[i] = i + 1;
}
int index = rand() % (n - 0);
a[n] = a[index];
for (int i = 0; i <= n; i++)
{
cout << a[i] << " ";
}
cout << endl;
int x1 = 0;
for (int i = 1; i <= n; i++)
{
x1 = x1 ^ i;
}
for (int i = 0; i <= n; i++)
{
x1 = x1 ^ a[i];
}
cout << x1 << endl;
cout << "----------------------------------"<<endl;
int* b = (int*)malloc(sizeof(int) * (n + 1));
for (int i = 0; i <= n; i++)
{
b[i] = 0;
}
for (int i = 0; i <= n; i++)
{
b[a[i]]++;
}
for (int i = 0; i <= n; i++)
{
if (b[i] == 2)
{
cout << i<<endl;
}
}
return 0;
}
开辅助空间的解法就是,先开一个数组,里面索引范围是我们连续的数据,遍历一遍有重复元素的数组,在对应索引的位置++,如果有一个位置是2,那我们就输出索引
2.找出落单的数
一个数组中除了一个数之外都出现了2次,找出那个数
思路还是一样的,我们一次异或,出现偶数次的都消掉了,奇数次的就会被保留下来,所以出现2次的都会被消掉,1次的就会保留
#include<iostream>
using namespace std;
int main()
{
int n;
cin >> n;
int* a = (int*)malloc(sizeof(int) * (n));
for (int i = 0; i < n; i++)
{
cin >> a[i];
}
int x1 = 0;
for (int i = 0; i < n; i++)
{
x1 = x1 ^ a[i];
}
cout << x1;
return 0;
}
3.找到某个数二进制中1的个数
思路1:我们给一个数K,每一回都减去一个1,然后和K按位与,这样可以消去一个1
我们只要数进行了多少次上述操作就行了
#include<iostream>
#include<string>
#include<string.h>
using namespace std;
int main()
{
int N;
cin >> N;
int count = 0;
while (N != 0)
{
N = ((N - 1) & N);
count++;
}
cout << count << endl;
return 0;
}
一开始我做的时候,实现转化成它的二进制(一个字符串),然后遍历这个字符串,找到1的个数,其实也是可以的,只是上述的方法更加简单
4.判断一个数是不是2的整数次
思路:如果二进制中只有一个1,那就是2的整数次
#include<iostream>
#include<string>
#include<string.h>
using namespace std;
int main()
{
int N;
cin >> N;
if (((N - 1) & N) == 0)
cout << N;
return 0;
}
5.将数进行奇数位偶数位互换
给出一个数,将其奇数位和偶数位互换,得出一个新的数
思路如下
代码
#include<iostream>
#include<string>
#include<string.h>
using namespace std;
int main()
{
int i = 0;
cin >> i;
int ou = i&0xaaaaaaaa;
int ji = i&0x55555555;
cout << ((ji << 1) ^ (ou >> 1))<<endl;
}