位运算相关基础
-
在处理整形数值时,可以直接对组成整形数值的各个位进行操作。这意味着可以使用屏蔽技术获得整数中的各个位
-
&(与)、|(或)、^(异或)、~(非)
与:都为1结果与1,或:有一个为1结果为1,异或:二者不同时结果为1 -
>>和<<运算符将二进制位进行右移或者左移操作
-
>>>运算符将用0填充高位;>>运算符用符号位填充高位,没有<<<运算符(Java)
-
对于int型,1<<35与1<<3是相同的(因为int型只有32位,超过32位的移动会按32位模截取,Java),而左边的操作数是long型时需对右侧操作数模64
应用
- 判断奇偶数
(x&1=1为奇,x&1=0为偶) - 获取二进制位是1还是0
(通过跟左移后的1进行与运算看结果为1还是0) - 交换两个整数变量的值
( 通过三次两两异或即可) - 不用判断语句,求整数的绝对值
补充:异或,可以理解为不进位加法:1+1=0,0+0=0,1+0=1
性质:
1、交换律,可任意交换运算因子的位置,结果不变
2、结合律(即(a^b)^c==a^(b^c))
3、对于任何数x,都有x^x=0,x^0=x
4、自反性A^B^B=A^0=A,连续和同一个因子做异或运算,最终结果为自己
题1:找出唯一成对的数
1-1000这1000个数放在含有1001个元素的数组中,只有唯一的一个元素值重复,其他均只出现一次。每个数组元素只能访问一次,设计一个算法,将它找出来;不同辅助存储空间,能否设计一个算法实现?
解题思路:
由上述性质3可知,任何数与自身异或为0,因此可以通过将这些数全部与自身异或一遍,结果即为那个奇数个的重复数。
代码:
#include<bits/stdc++.h>
#define random(x)(rand()%x)
using namespace std;
int main()
{
int N=1001;
int arr[N];
for(int i=0;i<N-1;i++)
{
arr[i]=i+1;
}
//随机选取一个数作为重复数
srand(int(time(0)));
arr[N-1]=(random(N-1))+1;
cout<<arr[N-1]<<endl;
int x=0;
for(int i=0;i<N;i++){
x=(x^arr[i])^i;
}
cout<<x<<endl;
return 0;
}
题2:二进制中1的个数
请实现一个函数,输入一个整数,输出该数二进制表示中1的个数例:9的二进制表示为1001,有2位是1
解题思路:
法1:利用上述“应用”中的第二例,通过该数与1的移位进行相与,判断是否为1,则count加1
法2:通过计算1001变位0需要消除几次1即可得到1的个数,
每次将该数减1,即可消除最低位的1,再与原数相与,直至相与结果为0
代码:
#include<bits/stdc++.h>
using namespace std;
int main()
{
//法一:
int count = 0;
int n;
cin>>n;
char str[32];//显示该数的二进制以验证
itoa(n,str,2);
cout<<str<<endl;
for(int i=0;i<32;i++){
if((n&(1<<i))==(1<<i)){
count++;
}
}
cout<<"法1的结果:"<<count<<endl;
//法2
count=0;
while(n!=0){
//每做一次与运算,消除最低位的1
n=(n-1)&n;
count++;
}
cout<<"法2的结果:"<<count<<endl;
return 0;
}
题3:将整数的奇偶位互换
解题思路:
将整数当成二进制,分别与101010…相与即可保留奇数部分a,与010101…相与即可保留偶数部分b,再将a右移1位,b左移1位,最后将a、b异或
代码:
#include<bits/stdc++.h>
using namespace std;
int m(int i){
int a = i&0xaaaaaaaa; //1010 1010 1010 ...奇数位
int b = i&0x55555555; //0101 0101 0101 ...偶数位
return (a>>1)^(b<<1);
}
int main()
{
int n;
cin>>n;
cout<<m(n)<<endl;
return 0;
}
题4:出现k次与出现1次
数组中只有一个数出现了1次,其他的数都出现了k次,请输出只出现了1次的数
解题思路:
两个相同的二进制数做不进位加法,结果为0
10个相同的10进制数做不进位加法,结果为0
k个相同的k进制数做不进位加法,结果为0
可以将这些数都转化为k进制进行不进位加法即可将出现了k次的数都变为0,而最终结果即为出现1次数的k进制再转化为10进制即可
代码:
#include<bits/stdc++.h>
using namespace std;
int main()
{
//在一组数中,只有一个数出现一次,其他数出现k次,求出这个数
//这里假设k=3
int arr[13]={2,2,2,9,7,7,7,3,3,3,6,6,6};
int len=13;
int k = 3;
//存储二进制数中最大的长度
int max_len=0;
int arr2[13][5]={0};
//将每个数字的二进制保存到二维数组中
for(int i=0;i<len;i++){
int temp=arr[i];
int j=0;
while(temp){
arr2[i][j]=temp%k;
temp/=k;
j++;
}
if(j>max_len){
max_len=j;
}
}
//定义接收k进制的结果数组并初始化
int *resArr=new int[max_len];
for (int i = 0; i < max_len; i ++){
resArr[i] = 0;
}
//将所有二进制数进行不进位加法
for(int i=0;i<len;i++){
for(int j=0;j<max_len;j++){
resArr[j]+=arr2[i][j];
}
}
//将k进制数转换为10进制
int res=0;
for(int j=0;j<max_len;j++){
res+=(resArr[j]%k)*pow(k,j);
}
cout<<res;
return 0;
}
PS:出现的小知识点总结:
- 随机数通过定义#define random(x)(rand()%x),并且要设置种子保证每次随机数不同srand(int(time(0))),通过random(N)可以获得[0,N)之间的随机数
要获取0-1的浮点数:rand()/(double)(RAND_MAX);
若要0~10的浮点数,则修改为rand() /(double)(RAND_MAX/10);
若要0~100浮点数,则修改为rand() /(double)(RAND_MAX/100) - 通过itoa(int,str,int)函数可以获得想要的进制串,第一个参数为需要转换的整数,第二个参数为接收转换后的字符数组如定义为char str[32],第三个为需要转换的进制数如2表示转换为2进制