位运算
位运算的分类
按位运算:左移、右移、按位与按位或、异或、 取反,取相反数
-
右移:
>>
右移一位相当于 /2 常见用法mid=(l-r)>>1+l;
用这种方法可以避免mid=(l+R)/2
l+R会超过int的范围的问题,并且位运算的速度比mid=(l-r)>>1+l;
的速度快很多 -
左移 :
<<
左移一位相当于 *2 没什么好说的就是速度比较快 -
按位与:
&
都为一为一其他为0、0&0=0
、0&1=1
、1&1=1
01010110
&
01001001
按位与的结果
01000000
-
按位或:
|
、1|1=1
、1|0=1
、0|0=0
-
异或:
^
,可以理解为无进位加法1^1=0
、0^1=1
、0^0=0
-
取反:
~
取反包含符号位0
全部变为1
,1
全部变为0
. -
取相反数:
-
,这是一个复杂的过程 有符号正数在内存中的存储为 原码第一位符号位为0
而负数在内存中的存储为补码 先把正数的符号位变为1然后去反加一就可以了
010011 //原码
101100//取反 第一位变为1表示负数
101101//加一变为对应的负数
位运算的应用
-n=~n+1;
一、得到一个数的最低位的1
如何得到一个数的最低位的1:
x=n;
t=x&(~n+1)
- 如何得到一个数的最高位:可以利用循环不断左移、知道为0记录个数n然后pow(2,n);或者下面这种方法,这一种学习的时候看了一个人按位运算的求法的播客但是找不到了,就没发了
int cnt=0;
int x;//带去最高位的数
while(x!=0)
{
x>>1;
}
int d=pow(2,n);
二、交换两个数字
- 交换两个数字可以利用异或的性质可以更加快速的交换:
a^a=0 0^b=b
arr[l]=arr[l]^arr[r];
arr[r]=arr[l]^arr[r];//arr[r]=arr[l];
arr[l]=arr[l]^arr[r];
这里面交换的两个值一定不能相等,不然会得到全0
这里第二句相当于 arr[r]=arr[l]^arr[r]^arr[r]
后面两个异或为0所以可以等效arr[r]=arr[l]
位运算不分顺序怎么来都是一样的。
三、打印一个数所有的二进制位
void ShowBit(int i)
{
// 这里要用无符号数不然下面循环判断条件会出错,因为1左移31位位
// 1000 0000 0000 0000 0000 0000 0000 0000 为负数
// 况且左移的时候如果为负数int类型的话会在最高位补符号位也会影响结果
unsigned int x = 1 << 31;
while (x > 0)
{
//&运算的优先级小于==的要加括号
if ((x & i) == 0)
{
printf("0");
}
else
{
printf("1");
}
x = x >> 1;
}
printf("\n");
}
四、把一个数对应Bit位位置置为1
//对应位变为1 1的第一位已经为1所以要把1左移i-1位
#define ReWriteBitO(x,i) (x=x|(1<<(i-1)) )
0000 0000 0000 0000 0000 0000 0000 0001
//左移i-1位 对应位置变为1
// 1和任何数或运算都为1
// 0和任何数运算都等于他自身 从而达到对于位置置1的目的
五、把一个数对应位置置0
#define RewriteBitZ(x,i) (x=x&(~(1<<i-1)) )
// 我们都知道0和任何数位&运算都为0
// 1和任何数位运算& 结果都为它本身
// 我们要把对应数位变为0我们就要得到对应数位为0其他数位为1的数
// 我们无法立刻得到这样的数
// 可以通过找对应数位为1其他为0然后取反的方法来完成