最近在看位运算的知识,十分感叹于位运算的博大精深,正好leetcode有 Bit Manipulation 的专题,正好拿来练练手。
Subsets
给出一个由不同的数字组成的数组,枚举它的子数组(子集)。
这道题我之前用递归解过,而且效率还不错(beat 83.33%),解法如下不加详述了:
/**
* @param {number[]} nums
* @return {number[][]}
*/
var ans, res, len;
function dfs(index, nums) {
ans.push(res.concat());
for (var i = index; i < len; i++) {
res.push(nums[i]);
dfs(i + 1, nums);
res.pop();
}
}
var subsets = function(nums) {
nums.sort(function(a, b) {
return a - b;
});
ans = [],
res = [],
len = nums.length;
dfs(0, nums);
return ans;
};
如果用位运算解,这是一道经典的枚举子集。
比如我们有数组[1, 2, 3]
,还是要用到标志位的概念,全集为1 1 1
,全集表示都取,它的子集有1 0 1
、 1 1 0
、 1 0 0
等等,我们通过枚举子集,然后再通过子集获取原来数组的元素即可。
比如子集 1 0 1
,我们可以依次获取最右边的1,然后根据获取的1的大小判断数组元素的index位置从而获取数组,之后把该1置为0即可。
/**
* @param {number[]} nums
* @return {number[][]}
*/
var subsets = function(nums) {
nums.sort(function(a, b) {
return a - b;
});
var all = (1 << nums.length) - 1
, ans = [];
// 枚举子集
for (i = all; i; i = (i - 1) & all) {
var tmp = i
, res = [];
while (tmp) {
var rightMostOne = tmp & (-tmp)
, index = Math.log(rightMostOne) / Math.log(2);
res.push(nums[index]);
tmp = tmp & (tmp - 1);
}
ans.push(res);
}
// 子集的枚举没有0,所以要special insert
ans.push([]);
return ans;
};
Power of Two
判断一个数是不是2的幂。如果一个数是2的幂,那么该数与上该数-1为0。我们以8举例:
1 0 0 0
0 1 1 1
很明显,上面两数做与运算的结果是0。但是有个特殊的情况是,0 & (0 - 1) === 0
,所以我们还得判断该数为正。
/**
* @param {number} n
* @return {boolean}
*/
var isPowerOfTwo = function(n) {
return (!(n & (n - 1)) && n > 0);
};
这里加强下,如果知道某数是2的幂,求解这个指数值。即Math.pow(2, x) = n
,求x的值。
也很简单,用个log的换底公式(其实没有涉及位运算):
return Math.log(n) / Math.IN2;
Missing Number
给出一个0~n组成的数组[0, 1, 2, 3 ... n]
,从中随即去掉一个数字,给你新的数组,求解被去掉的数字。比如给你[0, 1, 3]
,返回2
。
这题涉及^
运算的性质:
// if
a ^ b = c;
// then
a ^ c = b;
b ^ c = a;
解法也就呼之欲出了。还是假设数组[0, 1, 3]
,我们可以知道n为3(等于数组长度),从而可以计算出0 ^ 1 ^ 2 ^ 3
的值,我们把它赋值给c;然后我们计算所给数组的元素的异或值,赋值给a,假设被舍弃的元素为b,我们可以得到如下等式:
a ^ b = c;
根据前边所讲:
b = a ^ c;
完整代码:
/**
* @param {number} n
* @return {boolean}
*/
/**
* @param {number[]} nums
* @return {number}
*/
var missingNumber = function(nums) {
var a = nums.reduce(function(pre, item) {
return pre ^ item;
});
var b = nums.reduce(function(pre, item, index) {
return pre ^ index;
}, nums.length);
return a ^ b;
};