位运算
目录
位移运算
-
<<运算
-
a<<b 表示把a转为二进制后左移b位(在后面添加 b个0)。例如100的二进制表示为1100100,100左移2位后(后面加2个零):1100100<<2 =110010000 =400,可以看出,a<<b的值实际上就是a乘以2的b次方,因为在二进制数后面添加一个0就相当该数乘以2,2个零即2的2次方 等于4。通常认为a<<1比a*2更快,因为前者是更底层一些的操作。因此程序中乘以2的操作尽量用左移一位来代替。
-
定义一些常量可能会用到<<运算。你可以方便的用1<<16 -1 来表示65535(unsingned int 最大值16位系统)。很多算法和数据结构要求数据模块必须是2的幂,此时就可以用<<来定义MAX_N等常量。
-
-
>>运算
- 和<<相似,a>>b表示二进制右移b位(去掉末b位),相当于a除以2的b次方(取整)。我们经常用>>1来代替 /2(div 2),比如二分查找、堆的插入操作等等。想办法用>>代替除法运算可以使程序的效率大大提高。最大公约数的二进制算法用除以2操作来代替慢的出奇的%(mod)运算,效率可以提高60%。
-
n & 1 就是取最左边位
-
n >>= 1(n = n>>1)就是将n右移一位
数学运算转化为位运算:
-
向下整除 n//2 (C++中就是n/2)等价于右移一位 n >> 1;
-
取余数n%2 等价于判断二进制最右位 n & 1;
JS15 二进制中 1 的个数(位移运算)
class Solution {
public:
int hammingWeight(uint32_t n) {
unsigned int res =0; // c++ 使用无符号数
while(n!=0){
res += n & 1;
n >>= 1; // 将二进制数字 n 无符号右移一位
}
return res;
}
};
JS16 数值的整数次方:实现pow(x,n)(位移运算)
class Solution {
public:
double myPow(double x, int n) {
if(x == 0.0f) return 0.0;
long b = n; // 将n存入long变量,int32 变量区间n∈[−2147483648,2147483647] ,因此当n=−2147483648 时执行n=−n 会因越界而赋值出错。解决方法是先将 n 存入 long 变量 b,后面用 b 操作即可
double result = 1.0;
// 当 幂次为负时
if(b < 0){
x = 1/x;
b = -b;
}
//
while(b > 0){
if((b&1) == 1) result*=x;
x *= x;
b >>= 1;
}
return result;
}
};
JS56 数组中数字出现的次数I(异或运算)
class Solution {
public:
vector<int> singleNumbers(vector<int>& nums) {
int x=0, y=0; // 记录只出现一次的两个数字
int n =0; // 记录异或结果
int m =1; // 为了获取x和y异或结果中第一个1出现的位置,即从右到左 x和y第一个不同的位置
// 1.遍历nums进行异或,实际最后结果是x和y的异或结果
for(int num:nums){
n ^=num;
}
// 2.循环左移计算m即x和y异或结果中第一个1出现的位置
while((n & m) == 0){
m <<= 1;
}
// 4、通过m拆分nums为两个子数组,并进行异或
for(int num:nums){
if(num & m) x^= num; // 当num&m!=0
else y^=num; // 当num&m==0
}
// 5. 返回出现一次的数字
return vector<int> {x,y};
}
};
数组中数字出现的次数II(异或运算)
class Solution {
public:
int singleNumber(vector<int>& nums) {
int ones=0, twos =0;
for(int num:nums){
ones = ones ^ num & ~twos;
twos = twos ^ num & ~ones;
}
return ones;
}
};
JS65 不用加减乘除做加法
class Solution {
public:
int add(int a, int b) {
while(b !=0){
int c = (unsigned int)(a & b) << 1;
a ^= b;
b = c;
}
return a;
}
};