leetcode刷题日记-【77. 组合】

100 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第11天,点击查看活动详情

highlight: a11y-dark theme: channing-cyan

题目描述

给定两个整数 n 和 k,返回范围 [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

> 题目元素

  • 给定两个正整数,n、k。n代表有1。。。。n的数;
  • k代表需要从这n个数中找到k个组合成数组;
  • 返回所有有可能的组合方式

题目解析

将n个数进行排列组合,组合大小为k。排列组合的题一般采用回溯+递归进行解答。

  • 回溯:记录当前路径path,每次做完选择将当前选择放入path中,一条路径选择完成之后,退回到上次选择的节点,重新选择另外一条路径。

    回溯包含三个重要元素

    • 1、 选择路径: 已经做出的选择;
    • 2、 选择列表: 当前可以做的选择(还未选过的元素);
    • 3、 终止条件: 达到决策数底层,无法再做选择。
  • 首先确定本题的三要素,假定数组path为选择路径;
  • 选择列表就是原始数组中未被选择过的数组;
  • 终止条件就是path.length=k。

确定了三要素,接下来可以模拟一下代码流程。 n=4,k=2。则原始的数组为[1,2,3,4]。设原始的path=[]

  • 第一次选择为1,第二次选择为2,path=[1,2],此时path.length=k,所以此次递归调用直接return,选择回退;
  • 撤销第二次选择的2,选择3,此时path=[1,3],同理,此时path.length=k,此次递归也return,选择继续回退;
  • 撤销第二次选择的3,选择4,此时path[1,4],此时不仅path.length=k,而且选择列表也为空,所以这次选择直接结束,进入下次循环。

代码实现

/**
 *
 * @param n
 * @param k
 * @return
 */
public static List<List<Integer>> combine(int n, int k) {
    // 创建选择结果
    List<List<Integer>> res = new ArrayList<>();
    // k为0或者可选数组长度小于k时直接返回
    if (k == 0 || n < k) {
        return res;
    }
    // 创建当前路径
    ArrayDeque<Integer> path = new ArrayDeque<Integer>();
    // 从1开始遍历
    findPath(res,path,1,n,k);
    return res;
}

/**
 * 递归获取路径
 * @param res 放所有满足条件的路径
 * @param path 已选择的路径
 * @param i 当前遍历节点
 * @param n n
 * @param k k
 */
private static void findPath(List<List<Integer>> res, ArrayDeque<Integer> path, int i, int n, int k) {
    if (path.size() == k) {
        // 注意这里放入结果中的应该是个新数组,直接放入path引入的是path对象,会被后续操作改变
        res.add(new ArrayList<>(path));
        return;
    }
    for (;i <= n;i++) {
        // 做选择
        // 将当前元素放到队列末端
        path.addLast(i);
        findPath(res,path,i+1,n,k);
        // 回溯
        path.removeLast();
    }
}