LeetCode探索(47):77-组合

181 阅读1分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第4天,点击查看活动详情

题目

给定两个整数 nk,返回范围 [1, n] 中所有可能的 k 个数的组合。

你可以按 任何顺序 返回答案。

示例 1:

输入:n = 4, k = 2
输出:
[
  [2,4],
  [3,4],
  [2,3],
  [1,2],
  [1,3],
  [1,4],
]

示例 2:

输入:n = 1, k = 1
输出:[[1]]

提示:

  • 1 <= n <= 20
  • 1 <= k <= n

思考

这是一道有意思的题目,难度中等。

在思考如何解答这道题时,可以想到的是,如果k=2,我们可以借助两层for循环去遍历所有可能的组合,代码如下:

let n = 4;
for (let i = 1; i <= n; i++) {
    for (let j = i + 1; j <= n; j++) {
        console.log(i, j)
    }
}

这种思路有明显的局限性,如果k=3那么就需要3层for循环,随着k的增大我们无法写出符合条件的代码了...

我们可以借助回溯法,使用递归来解决嵌套层数的问题,将问题抽象为树形结构(N叉树),这样就容易理解很多了。由于之前没怎么接触这类问题,这里参考了官方答案去学习解题思路的。借用liweiwei1419的示意图,回溯法的思路如下所示:

这里我们逐个考虑数组[1, n]中的元素是否处于组合中,也就是深度优先遍历算法(DFS,即 Depth First Search)。对于不符合条件的情况我们通过剪枝去优化掉,合法的组合我们记录下来。

解答

方法一:递归实现组合型枚举

// 返回范围 [1, n] 中所有可能的 k 个数的组合
var combine = function(n, k) {
  const ans = [];
  const dfs = (cur, n, k, temp) => {
    // 剪枝:temp 长度不为 k 时返回
    if (temp.length + (n - cur + 1) < k) {
      return;
    }
    // 记录合法的答案
    if (temp.length == k) {
      ans.push(temp);
      return;
    }
    // 选择当前位置,记录cur后继续递归
    dfs(cur + 1, n, k, [...temp, cur]);
    // 不选择当前位置,跳过cur后继续递归
    dfs(cur + 1, n, k, temp);
  }
  dfs(1, n, k, []);
  return ans;
}

参考