初学者在学完位操作符之后,总是不能很好的掌握,因此这篇文章旨在巩固对位操作符的理解与使用。
有的题目可能会比较难以接受,但是看完一定会有收获
开始之前,先来了解一下位操作符
&按位与,只有两个数
同时为真才为真,否则为假
| 按位或,只有两个数同时为假才为假,否则为真
^按位异或,两个数相同为0,不同为0
题目大都来自牛客与力扣,虽然不能很好的囊括位操作符的全部,但是可以很好的加深理解。
一一一一一一一一一一一一一一分割线一一一一一一一一一一一一一一
注意:此题无链接
不能创建临时变量(第三个变量),实现两个数的交换。
a=10
;b=20
;
做这题首先要知道:
位操作符
支持交换律
且num^num=0
,0^num=num
那么我们就可以解决这道题
先来看代码实现,方便理解思路
代码实现:
#include
int main()
{
int a = 10;
int b = 20;
a = a^b;
b = a^b;
a = a^b;
printf("a = %d b = %d\n", a, b);
return 0;
}
思路:
将
a = a^b
的a带入b = a^b
中的a,即b=a^b^b
,则b=10
b = a^b
的b带入a =a^b
中的b,即a=a^a^b
,则a=20
一一一一一一一一一一一一一一分割线一一一一一一一一一一一一一一
首先我们要知道,
num & 1
为最后一位二进制的数字,
那我们使用移位操作符遍历一下整数的32位比特位
就迎刃而解
代码实现:
int convertInteger(int A, int B)
{
int count=0;//创建计数器
int i=0;
for(i=0;i<32;i++)
{
if((A>>i&1)!=(B>>i&1))
count++;
}
return count;
}
一一一一一一一一一一一一一一分割线一一一一一一一一一一一一一一
这题和上一题大同小异,都可以使用
位移操作符与位操作符遍历
解决,但这种方法必须循环32次
(整形情况下)才能得到结果
还有一种方法:
使用
num&(num-1)
,这个式子的意义是什么呢?
是将式子最右边的1
消掉
举个例子:
while(num)
{
num&(num-1);
}
//假设在while循环中,设num为15,那么二进制就为1111
1111 num
1110 num-1
1110 新的num
1101 num-1
1100 新的num
1011 num-1
1000 新的num
0111 num-1
0000 新的num
可以看到,当
num为0时
循环停止,循环了4
次,也就是1
的个数
我们就可以使用这种方法做题
思路:
使用
num&(num-1)
计算二进制1的个数
代码实现:
#include
int main()
{
int num = -1;
int i = 0;
int count = 0;//计数
while(num)
{
count++;
num = num&(num-1);
}
printf("二进制中1的个数 = %d\n",count);
return 0;
}
一一一一一一一一一一一一一一分割线一一一一一一一一一一一一一一
按位异或其实有个别名,叫做不进位加法
,什么意思呢?
就是可以计算不进位的加法
例如:
int a=10;
//0000 1010
int b=20;
//0001 0100
int sum=a^b;
//0001 1110也就是30,
//我们发现当没有进位时,按位异或可以代替加法,那么有进位怎么办呢?
不过我们要想解决这个题,仅仅知道不进位加法是不够的,
我们从10进制
举例
计算12+9
:
1.
1+0=0,2+9=1
(先不计算进位),结果为11
2.2+9
有进位,进位为10
3.两者相加:10+11=21
既然如此,我们也可以利用这种方法计算,那进位如何表示呢?
二进制中只有两个1
才会进位,因此我们按位与两个要相加的数
,再进行左移
就可以模拟进位。
思路:
1.将两个
要相加的数字按位异或
2.将两个数字进行按位与并向左移1位
计算出进位
3.将两数相加
,此时只需重复上述两个步骤,直到进位为0。
代码实现:
int Add(int num1, int num2 )
{
int sum=0;
int forward=0;
do
{
sum=num1^num2;
forward=(num1&num2)<<1;
num1=sum;//num1与num2顺序不重要
num2=forward;
}while(forward);
return sum;
}
一一一一一一一一一一一一一一分割线一一一一一一一一一一一一一一
在不创建临时变量交换整数,
我们了解了num^num=0
,0^num=num
,那我们这题就可以使用这种方法
#include
#include
int main()
{
int n=0;
scanf("%d",&n);
int arr[n];
//由编译器决定是否支持加长数组,
//牛客网支持,也可以不使用加长数组
//根据题目条件选择合适的个数范围
int ans=0;
for(int i=0;i<n;i++)
{
scanf("%d",&arr[i]);
ans^=arr[i];
}
printf("%d",ans);
return 0;
}
一一一一一一一一一一一一一一分割线一一一一一一一一一一一一一一
错误的集合,链接奉上
思路:
我们发现,重复的数字与消失的数字出现的次数都是偶数次(2和0次)
,其他的数字出现次数是奇数次(1次)
,此时我们可以多添加
一个从1~n
正确的数字集合,使重复的数字与缺失的数字为奇数次
(3和1次),其他的数字出现次数为偶数次
(2次),我们利用异或
就可以比较好的解决
设x,y分
别为重复的数字与消失的数字,将2n
个数字按位异或在一起,因为num^num=0
,0^num=num
,结果为x^y
,我们记为xor
因为x!=y
,故xor不为0
,我们此时令lowbit=xor&(-xor)
,这是为了取得x与y最低位不同比特位
(其实只要是不同的就可以,只是最低位好获得),简单的解释一下:
当x与y有最低位bit位不同时时,当前位异或的结果为1,那我们如何找到这个1呢
使用lowbit=xor&(-xor)
例如:
0000 1010
10的补码
1111 0110
-10的补码
0000 0010
按位与得到最低位不同比特位
得到lowbit
后,将2n
个数字分成两组
,第一组每个数字a
都满足a&lowbit==0
,第二组每个数字b
满足b&lowbit!=0
创建个2个变量,num1与num2,将第一组的a按位与在一起赋值给num1
,另一组同样赋值给num2,此时num1与num2就是x或y
,因为相同的数字肯定在一组,并且除了x与y出现的次数都是偶数次,故得到num1与num2就是x或y
此时将x与nums数组遍历比较一遍
,如果出现即为消失的数字
,否则相反。
代码实现:
int* findErrorNums(int* nums, int numsSize, int* returnSize)
{
static int arr[2];
int xor=0;
for(int i=1;i<=numsSize;i++)
{
xor^=nums[i-1];
xor^=i;
}
//得到x^y
int lowbit=xor&(-xor);
//得到最低位比特位
int num1=0;
int num2=0;
for(int i=0;i<numsSize;i++)//分组nums数组
{
if((nums[i]&lowbit)==0)
num1^=nums[i];
else
num2^=nums[i];
}
for(int i=1;i<=numsSize;i++)//分组添加的数字
{
if((i&lowbit)==0)
num1^=i;
else
num2^=i;
}
int count=0;//计数器
int i=0;
for(i=0;i<numsSize;i++)
{
if(nums[i]==num1)
count++;
}
if(count==0)
{
arr[1]=num1;
arr[0]=num2;
}
else
{
arr[1]=num2;
arr[0]=num1;
}
*returnSize=2;
return arr;
}
一些位运算中的简便运算
1.
x & 1
是奇数返回1
,是偶数返回零,可以放在if中判断奇偶
2.x |= 1<
等价于 x += pow(2,j)
;
3.x<<2 x<<1
,在十进制中表现的是乘上2的多少次方,在二进制中,就是先将这个x转换为二进制,然后整个数往前移位。(最后转化回去还是一样的)
4.num^num=0
,0^num=num
,异或也叫xor
在遇到数字重复时,二进制时,计算时,可能会有操作符的做法
遇到不会做的题很正常,不要感到沮丧,要知道我们也是站在巨人的肩膀上才能看得更远