每日算法:N数之和

高级前端进阶

共 4242字,需浏览 9分钟

 ·

2021-09-02 21:58


点击上方 三分钟学前端,关注公众号

回复交流,加入前端编程面试算法每日一题群


面试官也在看的前端面试资料

请用算法实现,从给定的无需、不重复的数组A中,取出N个数,使其相加和为M。并给出算法的时间、空间复杂度,如:

var arr = [1471198106];
var N = 3;
var M = 27;

Result:
[7119], [11106], [9810]

解题思路:利用二进制

根据数组长度构建二进制数据,再选择其中满足条件的数据。

我们用 10 来表示数组中某位元素是否被选中。因此,可以用 0110 来表示数组中第 1 位和第 2 位被选中了。

所以,本题可以解读为:

  • 数组中被选中的个数是 N
  • 被选中的和是 M

我们的算法思路逐渐清晰起来:遍历所有二进制,判断选中个数是否为 N ,然后再求对应的元素之和,看其是否为 M

1. 从数组中取出 N 个数

例如:

var arr = [1234];
var N = 3;
var M = 6;

如何判断 N=3 是,对应的选取二进制中有几个 1 喃?

最简单的方式就是:

const n = num => num.toString(2).replace(/0/g'').length

这里我们尝试使用一下位运算来解决本题,因为位运算是不需要编译的(位运算直接用二进制进行表示,省去了中间过程的各种复杂转换,提高了速度),肯定速度最快。

我们知道 1&0=01&1=11111&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 转化成 01001110 & 0100  为 1100, 大于 0 ,因此下标 1 在。而 1110 & 00010 ,因此 下标 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
}

最后

欢迎关注「三分钟学前端」,回复「交流」自动加入前端三分钟进阶群,每日一道编程算法面试题(含解答),助力你成为更优秀的前端开发!

号内回复:

网络」,自动获取三分钟学前端网络篇小书(90+页)
JS」,自动获取三分钟学前端 JS 篇小书(120+页)
算法」,自动获取 github 2.9k+ 的前端算法小书
面试」,自动获取 github 23.2k+ 的前端面试小书
简历」,自动获取程序员系列的 120 套模版
》》面试官也在看的前端面试资料《《
“在看和转发”就是最大的
浏览 129
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报