位运算的操作数必须是整数,当二元位运算的操作数是不同类型的整数时,也会自动进行类型转换。
n&(n-1)作用:将n的二进制表示中的最低位为1的改为0,先看一个简单的例子: n = 10100(二进制),则(n-1) = 10011 ==》n&(n-1) = 10000 可以看到原本最低位为1的那位变为0。 弄明白了n&(n-1)的作用,那它有哪些应用? 1. 求某一个数的二进制表示中1的个数 while (n >0 ) { count ++; n &= (n-1); }
2. 判断一个数是否是2的方幂 n > 0 && ((n & (n - 1)) == 0 )
3. 计算N!的质因数2的个数。 容易得出N!质因数2的个数 = [N / 2] + [N / 4] + [N / 8] + .... 下面通过一个简单的例子来推导一下过程:N = 10101(二进制表示) 现在我们跟踪最高位的1,不考虑其他位假定为0, 则在 [N / 2] 01000 [N / 4] 00100 [N / 8] 00010 [N / 8] 00001 则所有相加等于01111 = 10000 - 1 由此推及其他位可得:(10101)!的质因数2的个数为10000 - 1 + 00100 - 1 + 00001 - 1
= 10101 - 3(二进制表示中1的个数)
推及一般N!的质因数2的个数为N - (N二进制表示中1的个数)
目前看到只有这些应用,但只要理解了n&(n-1)的原理及作用,在碰到相关问题时也会比较容
易解决。
4.线段树中lowbitn&(-n)表示最后一个1的基数
5.按位与运算通常用来将某变量中的某些位清0或保留某些位不变。
例如,如果需要将int型变量n的低8位全置成0,而其余位不变,则可以执行:
n = n & 0xffffff00;
也可以写成:
n &= 0xffffff00;
如果n是short类型的,则只需执行:
n &= 0xff00;
如何判断一个int型变量n的第7位(从右往左,从0开始数)是否是1 ?
只需看表达式 “n & 0x80”的值是否等于0x80即可
6.按位或运算符“|”是双目运算符。
功能:将参与运算的两操作数各对应的二进制位进行或操作,只有对应的两个二进位都为0时,结果的对应二进制位才是0,否则为1。
例如:表达式“21 | 18 ”的值是23(即二进制数10111)。
按位或运算通常用来将某变量中的某些位置1或保留某些位不变。
例如,如果需要将int型变量n的低8位全置成1,而其余位不变,则可以执行:
n |= 0xff;
7.按位异或运算符“^”是双目运算符。
功能:将参与运算的两操作数各对应的二进制位进行异或操作,即只有对应的两个二进位不相同时,结果的对应二进制位才是1,否则为0。
例如:表达式“21 ^ 18 ”的值是7(即二进制数111)。
异或运算的特点
如果 a^b=c,那么就有 c^b = a以及c^a=b。
此规律可以用来进行最简单的加密和解密
用途:
使特定位翻转(与0异或保持原值,与1异或取反)
例如:要使 01111010 低四位翻转:
0 1 1 1 1 0 1 0
(^) 0 0 0 0 1 1 1 1
0 1 1 1 0 1 0 1
由于异或运算的自反性和满足交换律、结合律,常常被用于一些技巧性较强的应用中.
void inplace_swap(int* x, int* y)
{
*y = *x ^ *y;
*x = *x ^ *y;
*y = *x ^ *y;
}
Q:1-1000放在含有1001个元素的数组中,只有唯一的一个元素值重复,其它均只出现一次.每个数组元素只能访问一次,设计一个算法,将它
找出来;不用辅助存储空间
方法一:
将1001个数加起来,再减去1+2+3+…+1000,不过这种方法不通用,容易溢出。
方法二:
将1001个数异或,然后和1^2^3^…^1000的结果进行异或,最后的结果就是要求的数。(a^a=0,0^a=a)
google面试题的变形:一个数组存放若干整数,一个数出现奇数次,其余数均出现偶数次,找出这个出现奇数次的数?
解法有很多,但是最好的和上面一样,就是把所有数异或,最后结构就是要找的,原理同上!
找出数组中唯一不重复的元,数组可以很大。(和奇数次一样)
8.按位非运算符“~”是单目运算符。
其功能是将操作数中的二进制位0变成1,1变成0。
例如,表达式“~21”的值是无符号整型数 0xffffffea,
而下面的语句: printf("%d,%u,%x",~21,~21,~21);(无符号时,直接加权加和)
输出结果就是:-22,4294967274,ffffffea
9.例如,常数9有32位,其二进制表示是: 0000 0000 0000 0000 0000 0000 0000 1001
因此,表达式“9<<4”的值,就是将上面的二进制数左移4位,得: 0000 0000 0000 0000 0000 0000 1001 0000 即为十进制的144。
实际上,左移1位,就等于是乘以2,左移n位,就等于是乘以2n。而左移操作比乘法操作快得多。
特别注意:有符号数的左移溢出情况。
10.实际上,右移n位,就相当于左操作数除以2n,并且将结果往小里取整。
11.
1<<n=2^n;n<<1(2*n)
有两个int型的变量a和n(0 <= n <= 31),
要求写一个表达式,使该表达式的值和a的第n位相同。
返回结果0或者1,不是那一位的权值,a & (1 << n )是a的第n位的权值;
结果:(a & (1 << n )) >> n
12.检测一个无符号数是否为2^n-1(^为幂): x&(x+1),或者(x+1)%2,前者是的话返回0,否则返回x,测试了下有符号的也可。
将最右侧0变为1:x | (x+1),相反的是x&(x-1)。
13.二进制进补码运算:
-x = ~x + 1 = ~(x-1)
~x = -x-1
-(~x) = x+1
~(-x) = x-1
x+y = x - ~y - 1 = (x|y)+(x&y)
x-y = x + ~y + 1 = (x&~y)-(~x&y)
x^y = (x|y)-(x&y)(异或)
x|y = (x&~y)+y
x&y = (~x|y)-~x
x==y: ~(x-y|y-x)
x!=y: x-y|y-x
x< y: (x-y)^((x^y)&((x-y)^x))
x<=y: (x|~y)&((x^y)|~(y-x))
x< y: (~x&y)|((~x|y)&(x-y))//无符号x,y较
x<=y: (~x|y)&((x^y)|~(y-x))//无符号x,y较