一、题目
对数组 nums
执行 按位与 相当于对数组 nums
中的所有整数执行 按位与 。
- 例如,对
nums = [1, 5, 3]
来说,按位与等于1 & 5 & 3 = 1
。 - 同样,对
nums = [7]
而言,按位与等于7
。
给你一个正整数数组candidates
。计算candidates
中的数字每种组合下 按位与 的结果。candidates
中的每个数字在每种组合中只能使用 一次 。
返回按位与结果大于 0
的 最长 组合的长度。
示例 1:
输入:candidates = [16,17,71,62,12,24,14]
输出:4
解释:组合 [16,17,62,24] 的按位与结果是 16 & 17 & 62 & 24 = 16 > 0 。
组合长度是 4 。
可以证明不存在按位与结果大于 0 且长度大于 4 的组合。
注意,符合长度最大的组合可能不止一种。
例如,组合 [62,12,24,14] 的按位与结果是 62 & 12 & 24 & 14 = 8 > 0 。
示例 2:
输入:candidates = [8,8]
输出:2
解释:最长组合是 [8,8] ,按位与结果 8 & 8 = 8 > 0 。
组合长度是 2 ,所以返回 2 。
提示:
- 1 <= candidates.length <= 105
- 1 <= candidates[i] <= 107
二、思路
找出符合条件的组合的特点
以 示例1的组合 [16,17,62,24]
为例
十进制 | 对应二进制 |
---|---|
16 | 00010000 |
17 | 00010001 |
62 | 00111110 |
24 | 00011000 |
通过观察,可以发现:按位与的结果要大于0,得保证组合中某一位都为1
结论:统计二进制中的每一位上,为1的整数个数,个数最大值即为本题结果
三、题解
方法1(菜鸟版,可忽略)
- 利用
toString(2)
,将所有整数降序排列后,转为二进制字符,存入到一个新的数组bCandidates
- 用双重for循环,外层循环遍历位,执行条件为:小于最大整数的二进制字符串长度
bCandidates[0].length
,用occurNum
记录该位上为1
的整数个数 - 内层循环遍历
bCandidates
,若bCandidates[j]
的第i
个字符为1,occurNum++
;
由于数组是降序排列的,若bCandidates[j]
的第i
个字符为undefined
,则后续的bCandidates[j+1]
、bCandidates[j+2]
…上的第i
个字符一定也为undefined
,所以可以使用break
,跳出内层循环
/**
* @param {number[]} candidates
* @return {number}
*/
var largestCombination = function (candidates) {
candidates.sort((a, b) => b - a)
let bCandidates = candidates.map(item => item.toString(2))
const iLength = bCandidates[0].length
let res = 0,sum;
for (let i = 0; i < iLength; i++){
sum = 0;
for (let j = 0; j < bCandidates.length; j++){
let str = bCandidates[j]
let strLength = str.length
if (str[strLength - i - 1] === '1') {
sum ++
} else if (str[strLength - i - 1] === undefined) {
break
}
}
res = Math.max(res, sum)
}
return res
};
方法2(进阶简洁版)
方法1中,我们判断整数的二进制第 i 位是否为1:先将整数转为二进制字符串,再获取字符串的第i位的值。
其实我们可以利用 按位与&
和 左移运算符<<
更快速的判断candidates[j] & (1 << i)
/**
* @param {number[]} candidates
* @return {number}
*/
var largestCombination = function (candidates) {
let res = 0, sum;
for (let i = 0; i < 32; i++) {
sum = 0;
for (let j = 0; j < candidates.length; j++) {
if (candidates[j] & (1 << i)) {
// candidate[i] 的第 i 位是 1
sum++;
}
}
res = Math.max(res, sum)
}
return res;
};
性能大幅提升!nice
四、知识拓展
1、& 和 && 的区别
按位与& :
- a&b是把 a 和 b 都转换成二进制数然后再进行与的运算
- &对所有表达式都得判断。
逻辑与&&:
- a&&b就是当且仅当两个操作数均为 true时,其结果才为 true,只要有一个为false,a&&b就为false。
- &&进行的是短路判断,即第一个表达式不成立的话,后面的表达式不运算,直接返回。
2、<< 左移运算符
左移运算符(<<)表示将一个数的二进制值向左移动指定的位数,尾部补0,即乘以2的指定次方(最高位即符号位不参与移动)。
例:表达式14 << 2
的值为56,因为14(即二进制的00001110)向左移两位等于56(即二进制的00111000)
3、js中十进制和二进制的转换
var num = 10;
num.toString(2); // 十进制转二进制
parseInt(num, 2); // 二进制转十进制;num 被看做是二进制的数
parseInt(num, 2).toString(8); // 二进制转八进制;也可以看做是二进制先转成十进制,再转成八进制