力扣77 组合

题目:

给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。
你可以按 任何顺序 返回答案。

示例:

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

思路:

  要解决 n为100,k为50的情况,暴力写法需要嵌套50层for循环,那么回溯法就用递归来解决嵌套层数的问题递归来做层叠嵌套(可以理解是开k层for循环),每一次的递归中嵌套一个for循环,那么递归就可以用于解决多层嵌套循环的问题了

  每次从集合中选取元素,可选择的范围随着选择的进行而收缩,调整可选择的范围图中可以发现n相当于树的宽度,k相当于树的深度图中每次搜索到了叶子节点,我们就找到了一个结果。相当于只需要把达到叶子节点的结果收集起来,就可以求得 n个数中k个数的组合集合。

class Solution {
    //组合问题:N个数里面按一定规则找出k个数的集合
    List<List<Integer>> res=new ArrayList<>();
    LinkedList<Integer> path=new LinkedList<>();
    public List<List<Integer>> combine(int n, int k) {
        combineback(n,k,1);
        return res;
    }

    //1.回溯函数模板返回值以及参数
    public void combineback(int n,int k, int startIndex){
        //2.回溯函数终止条件
        if(path.size()==k){//找到叶子节点
            res.add(new ArrayList<>(path));
            //res.add(new ArrayList(path)):开辟一个独立地址,地址中存放的内容为path集合,后续path的变化不会影响到res。
            //res.add(path):将res尾部指向了path地址,后续path内容的变化会导致res的变化。
            return;
        }
        //3.回溯搜索的遍历过程
        for(int i=startIndex;i<=n;i++){
            path.add(i);//处理节点
            combineback(n, k, i + 1);//递归
            path.removeLast();//回溯,撤销处理结果
        }
    }
}

剪枝处理

  看一下优化过程如下:

  1. 已经选择的元素个数:path.size();

  2. 还需要的元素个数为: k - path.size();

  3. 在集合n中至多要从该起始位置 : n - (k - path.size()) + 1,开始遍历

  为什么有个+1呢,因为包括起始位置,我们要是一个左闭的集合。

遍历部分可改为:

for (int i=startIndex;i<=n-(k-path.size())+1;i++){
    path.add(i);
    combineHelper(n, k, i + 1);
    path.removeLast();
}

 

posted @ 2023-02-28 20:32  壹索007  阅读(13)  评论(0编辑  收藏  举报