代码随想录算法训练营第24天 | LeetCode77.组合

题目链接:77. 组合 - 力扣(LeetCode)

作者思考:

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

 首先组合问题要先搞清楚什么是组合,组合是元素无序的存放到集合中,和排列不一样,排列是有序的。在组合中[1,2]和[2,1]是同一种情况,但是在排列中就是属于两种情况。

搞清楚什么是组合后,我们再来分析问题。

如果k比较少的情况,我们可以直接for循环进行嵌套,那么如果k比较大,那么我们就要嵌套k层for循环,这想想也是不现实的。那么用什么办法好呢?

这时就可以用我们之前学到的回溯思想。

如图

 有递归就有回溯,可以说回溯是递归的附加物。

那么说到了递归,肯定要说自我问答一下,递归三部曲。

①确定返回值和参数

本题的结果集是List<List<Integer>> 那么我们用result来接收这个结果;树枝就是路径,用List<Integer> path;定义一个变量来表示 下一次的元素将要进行组合的位置。

②确定终止条件

在上图中,每次集合中存够了k个元素就 退出当前的递归。那么终止条件是

if (path.size() == k) {//当单个结果长度等于目标k的长度
            result.add(new ArrayList<>(path));//将path添加到结果集中
            return ;
}

③单层递归逻辑

我们的递归逻辑是表示二叉树的深度,单层的递归逻辑就是二叉树的宽度(横向)。

for (int i = startindex; i <= n ; i++) {
            path.add(i);
            backtracking(n, k, i +1);//递归剩下元素
            path.remove(path.size() -1);//回溯的过程 将本次添加元素删除 给下一个元素留位置
}

完整代码

class Solution {
    List<List<Integer>> result = new ArrayList<>();//存放结果集
    List<Integer> path = new ArrayList<>();//存放单个结果
    public List<List<Integer>> combine(int n, int k) {
        backtracking(n, k, 1);
        return result;
    }
    void backtracking(int n, int k, int startindex) {
        //确定递归的终止条件
        if (path.size() == k) {//当单个结果长度等于目标k的长度
            result.add(new ArrayList<>(path));//将path添加到结果集中
            return ;
        }
        for (int i = startindex; i <= n; i++) {
            path.add(i);
            backtracking(n, k, i +1);//递归剩下元素
            path.remove(path.size() -1);//回溯的过程 将本次添加元素删除 给下一个元素留位置
        }
        return ;
    }
}

本题由于是组合问题,我们想想可不可以进行剪枝优化。

如图,为什么要进行剪枝操作:当我们n = 4, k = 4的时候,进行第一层for循环后,取2操作已经没有意义了,因为组合中的无序性,在取2的时候元素中不可能出现元素1,那集合的长度也不可能符合题目要求k = 4。如果我们不进行优化,我们需要进行整树的遍历,实际上该例子我们只遍历了树的一条树枝,之后的遍历都没有意义。

 故我们只需要优化单层递归的范围。

1.选取过的元素的集合长度:path.size()

2.未选取过的元素的集合长度:k - path.size()

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

优化后完整代码

class Solution {
    List<List<Integer>> result = new ArrayList<>();//存放结果集
    List<Integer> path = new ArrayList<>();//存放单个结果
    public List<List<Integer>> combine(int n, int k) {
        backtracking(n, k, 1);
        return result;
    }
    void backtracking(int n, int k, int startindex) {
        //确定递归的终止条件
        if (path.size() == k) {//当单个结果长度等于目标k的长度
            result.add(new ArrayList<>(path));//将path添加到结果集中
            return ;
        }
        for (int i = startindex; i <= n - (k - path.size()) + 1; i++) {
            path.add(i);
            backtracking(n, k, i +1);//递归剩下元素
            path.remove(path.size() -1);//回溯的过程 将本次添加元素删除 给下一个元素留位置
        }
        return ;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值