每日算法:N数之和
回复交流,加入前端编程面试算法每日一题群
请用算法实现,从给定的无需、不重复的数组A中,取出N个数,使其相加和为M。并给出算法的时间、空间复杂度,如:
var arr = [1, 4, 7, 11, 9, 8, 10, 6];
var N = 3;
var M = 27;
Result:
[7, 11, 9], [11, 10, 6], [9, 8, 10]
解题思路:利用二进制
根据数组长度构建二进制数据,再选择其中满足条件的数据。
我们用 1 和 0 来表示数组中某位元素是否被选中。因此,可以用 0110 来表示数组中第 1 位和第 2 位被选中了。
所以,本题可以解读为:
数组中被选中的个数是 N。被选中的和是 M。
我们的算法思路逐渐清晰起来:遍历所有二进制,判断选中个数是否为 N ,然后再求对应的元素之和,看其是否为 M 。
1. 从数组中取出 N 个数
例如:
var arr = [1, 2, 3, 4];
var N = 3;
var M = 6;
如何判断 N=3 是,对应的选取二进制中有几个 1 喃?
最简单的方式就是:
const n = num => num.toString(2).replace(/0/g, '').length
这里我们尝试使用一下位运算来解决本题,因为位运算是不需要编译的(位运算直接用二进制进行表示,省去了中间过程的各种复杂转换,提高了速度),肯定速度最快。
我们知道 1&0=0 、 1&1=1 ,1111&1110=1110 ,即 15&14=14 ,所以我们每次 & 比自身小 1 的数都会消除一个 1 ,所以这里建立一个迭代,通过统计消除的次数,就能确定最终有几个 1 了:
const n = num => {
  let count = 0
  while(num) {
    num &= (num - 1)
    count++
  }
  return count
}
2. 和为 M
现在最后一层判断就是选取的这些数字和必须等于 M ,即根据 N 生成的对应二进制所在元素上的和 是否为 M
比如 1110 ,我们应该判断 arr[0] + arr[1] + arr[2] 是否为 M。
那么问题也就转化成了如何判断数组下标是否在 1110 中呢?如何在则求和
其实也很简单,比如下标 1 在,而下标 3 不在。我们把 1 转化成 0100 , 1110 & 0100  为 1100, 大于 0 ,因此下标 1 在。而 1110 & 0001 为 0 ,因此 下标 3 不在。
所以求和我们可以如下实现:
let arr = [1,2,3,4]
// i 为满足条件的二进制
let i = 0b1110
let s = 0, temp = []
let len = arr.length
for (let j = 0; j < len; j++) {
  if ( i & 1 << (len - 1 - j)) {
 s += arr[j]
 temp.push(arr[j])
  }
}
console.log(temp)
// => [1, 2, 3]
最终实现
// 参数依次为目标数组、选取元素数目、目标和
const search = (arr, count, sum) => {
  // 计算某选择情况下有几个 1,也就是选择元素的个数
  const getCount = num => {
    let count = 0
    while(num) {
      num &= (num - 1)
      count++
    }
    return count
  }
  let len = arr.length, bit = 1 << len, res = []
  
  // 遍历所有的选择情况
  for(let i = 1; i < bit; i++){
    // 满足选择的元素个数 === count
    if(getCount(i) === count){
      let s = 0, temp = []
      // 每一种满足个数为 N 的选择情况下,继续判断是否满足 和为 M
      for(let j = 0; j < len; j++){
        // 建立映射,找出选择位上的元素
        if(i & 1 << (len - 1 -j)) {
          s += arr[j]
          temp.push(arr[j])
        }
      }
      // 如果这种选择情况满足和为 M
      if(s === sum) {
        res.push(temp)
      }
    }
  }
  return res
}最后
号内回复:
120 套模版评论
