位运算相关内容整理
位运算相关内容整理
1) 负数
负数的右移:负数右移的话,由于要保持它是负数,所以负数的二进制的左边补1。如果一直右移的话,最后就就变成0xFFFFFFFF 即-1
如: -4>>1 为-2 ;-4>>2为-1
负数的左移:跟正整数左移一样,右边补0。左移总是在低位补零,高位丢失,因而负数左移后可能会变成正数。
int x = 0x8fff0000;
cout << (x << 1); // 输出为536739840
cout << (-2 << 31); // 输出为0
2)位运算n&(n-1)的妙用
i) 判断一个数是否是2的方幂 –-
n>0 && ((n&&(n-1)) == 0)
ii) 求某一个数的二进制表示中1的个数 --
这里实际上是一种数字分析:把一个整数减去1,再和原整数做与运算,会把该整数最右边的一个1变成0。那么一个整数的二进制表示中有多少个1,就可以进行多少次这样的操作。
/*fun返回值是参数n转化为二进制后包含1的数量 要注意,此例中形参的unsigned不可省略。 这是因为while循环里有n>0的比较操作,若将形参声明为int,则负数无法得到结果 【将int转化为unsigned int,并不会改变内存中数据的存储内容,只是解释不同】*/ int fun(unsigned int n) { int cnt = 0; while (n > 0) { n &= (n - 1); cnt++; } return cnt; } /*或者可以写成如下形式*/ // -- 推荐吧!! int fun2(int n) { int cnt = 0; while (n != 0) { n &= (n - 1); cnt++; } return cnt; }
另外,可以推广至:1)判断一个整数是不是2的整数次方。——if(n&(n-1)==0);2)两个整数m和n,计算需要改变多少次m的二进制表示,才能够得到n(先异或,再求异或的结果二进制表示中1的个数)。
iii) 计算N!的质因数2的个数
容易得出N!质因数2的个数 = [N / 2] + [N / 4] + [N / 8] + ....
下面通过一个简单的例子来推导一下过程:N = 10101(二进制表示)
现在我们跟踪最高位的1,不考虑其他位,假定都为0,
[N / 2] 01000
[N / 4] 00100
[N / 8] 00010
[N / 16] 00001
则所有相加等于01111 = 10000 - 1
由此推及其他位可得:(10101)!的质因数2的个数为10000 - 1 + 00100 - 1 + 00001 - 1 = 10101 - 3(二进制表示中1的个数)
推及一般N!的质因数2的个数为N-(N二进制表示中1的个数)
3)关于异或运算符。
i)
异或运算符有一个奇特的性质:两个相同的数异或之后结果为0,且满足交换律。即:若有A^B^C^D^E^F^B,等价于A^C^D^E^F。
这一性质常用于寻找成对出现时缺失的某一个数。
例题:给你一个由n-1个整数组成的为排序的序列,其元素都是1-n中的不同整数。请写出寻找序列中缺失整数的线性时间算法。
解答:若用n个数的和减去n-1个数的和,可能有溢出。
因此,用异或方法。首先求得n个数的异或结果,在用题中所给的序列与之前的结果求异或。最终得到的就是丢失的整数。
ii)不使用第三方变量交换两个整数a和b。
a=a^b;
b=a^b;
a=a^b;
iii)不用加减乘除做加法
int add_no_op(int num1, int num2) { if (num2 == 0) return num1; int sum = num1^num2; int carry = (num1&num2) << 1; return add_no_op(sum, carry); }
iv)用位运算操作求两个数的均值
(x&y)+(x^y)>>2;