位运算的常见面试 原文:github.com/jackshen310…
位运算基础
| 符号 | 描述 | 运算规则 |
|---|---|---|
| & | 与 | 两个位都为1时,结果才为1 |
| | | 或 | 两个位都为0时,结果才为0 |
| 异或 | 两个位相同为0,相异为1 | |
| ~ | 取反 | 0变1,1变0 |
| << | 左移 | 各二进位全部左移若干位,高位丢弃,低位补0 |
| >> | 右移 | 各二进位全部右移若干位,对无符号数,高位补0,有符号数,各编译器处理方法不一样,有的补符号位(算术右移),有的补0(逻辑右移) |
判断整数的奇偶性
奇数的二进制第一位是1,偶数的二进制第一位是0,比如5的二进制表示101的第一位是1,4的二进制表示100的第一位是0
// 普通函方式
if(n % 2 == 0) {
// n 是偶数
} else {
// n 是奇数
}
// 用位运算 &
if( n & 1 == 0) {
// n 是偶数
} else {
// n 是奇数
}
总结:使用 n & 1 == 1 判断奇偶数
取中位数
在二分法中,经常要取中位数,可以用移位来实现
function getMid(a,b) {
return (a + b) >> 1;
}
console.log(getMid(1,5)); // 3
console.log(getMid(1,4)); // 2
判断整数是不是2的整数次幂
如果n是2的整数次幂,那么n的二进制表示中只有最高位是1,其它位都是0,比如4 => 100,16 => 10000,n - 1 的二进制除了高位变成0,其它位都变成1,如(4-1)=> 011,(16-1) => 01111,此时n & (n-1) == 0
总结:使用 n & (n-1) == 0 判断是不是2的整数次幂
计算整数二进制表示中的1的个数
leetcode原题:leetcode-cn.com/problems/er…
// 4二进制表示100,它只有一个1,11的二进制表示1011,他有3个1,我们用(n -1) & n 就可以将右边第一个1设置为0,比如1011 & 1010 => 1010, 1010 & 1001 => 1000, 1000 & 0111 => 0,三次之后结果为0,则有3个1。
let res = 0;
while(n != 0){
n &= (n - 1);
res++;
}
return res;
总结:n & (n – 1) ,可以将最右边的 1 设置为 0
在其他数都出现两次的数组中找到只出现一次的那个数
function singleNum(arr) {
let res = 0;
arr.forEach( v => {
res = res ^ v;
});
return res;
}
console.log(singleNum([1,0,1,3,0])); // 3
在其他数都出现两次的数组中找到只出现一次的那两个数
leetcode原题:leetcode-cn.com/problems/sh…
/**
把两个只出现一次的数记为a、b
1、将数组中所有元素进行异或操作,因为相同的数异或为0,这样得到的结果就是a异或b的值。
2、因为a和b肯定不相等,所以第一步得到的结果肯定不为0.也就是说此结果写成二进制至少有一位是1,找到这个为1的下标。用这一位我们可以把数组中的数分成两部分,一部分是这一位为1的数,一部分是这一位为0的数。a和b肯定不在同一个部分。数组中原来相同的数肯定在同一个部分。
3、将这两部分数分别进行异或运算。最后每部分异或的结果就是a和b。
*/
function FindNumsAppearOnce(array) {
// write code here
let diff = 0;
array.forEach((v) => {
diff = diff ^ v;
});
// 位运算 diff & -diff 能得到 diff 位级表示中最右侧为 1 的位
diff = diff & -diff;
let first = 0;
let second = 0;
array.forEach((v) => {
//
if ((diff & v) == 0) {
first = first ^ v;
} else {
second = second ^ v;
}
});
if (first > second) {
return [second, first];
} else {
return [first, second];
}
}
console.log(FindNumsAppearOnce([1,0,1,3,0,4])); // [3,4]
总结:两个相同的数异或为0(n ^ n == 0), n & (-n) 能得到 n 位级表示中最右侧为 1 的位
其它
- 使用 (x ^ y) >= 0 来判断符号是否相同。(如果两个数都是正数,则二进制的第一位均为0,x^y=0;如果两个数都是负数,则二进制的第一位均为1;x^y=0 如果两个数符号相反,则二进制的第一位相反,x^y=1。有0的情况例外,^相同得0,不同得1)
- i+(~i)=-1,i 取反再与 i 相加,相当于把所有二进制位设为1,其十进制结果为-1。