LeetCode题练习与总结:组合-77

一、题目描述

给定两个整数 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

二、解题思路

要解决这个问题,我们可以使用回溯算法。回溯算法是一种通过探索所有可能的候选解来找出所有的解的算法。如果候选解被确认不是一个解(或者至少不是最后一个解),回溯算法会通过在上一步进行一些变化丢弃该解,即回溯并且再次尝试。

为了生成所有可能的组合,我们可以从数字1开始,依次尝试每一个数字,对于每一个数字,我们都可以选择将其放入当前的组合中,或者不放入。如果放入,我们就继续向组合中添加下一个数字;如果不放入,我们就尝试下一个数字。当我们组合中的数字数量达到k时,我们就找到了一个有效的组合,将其添加到结果列表中。

具体步骤如下:

1. 初始化一个结果列表result,用于存储所有有效的组合。

2. 定义一个回溯函数backtrack,该函数接受以下参数:

  • result:结果列表
  • comb:当前组合
  • start:当前尝试的数字
  • n:数字范围上限
  • k:组合大小

3. 在backtrack函数中,首先检查当前组合的大小是否等于k:

  • 如果是,说明找到了一个有效的组合,将其添加到结果列表中,并返回。
  • 如果不是,继续执行下一步。

4. 从start开始,遍历所有可能的数字:

  • 将当前数字添加到当前组合中。
  • 递归调用backtrack函数,尝试下一个数字,即start + 1
  • 在递归调用之后,将最后一个数字从当前组合中移除,以便尝试下一个数字。

5. 在combine函数中,首先检查k是否小于等于0或n是否小于k:

  • 如果是,返回空的结果列表。
  • 如果不是,从1开始调用backtrack函数。

这样,我们就可以生成所有的组合,并将它们存储在结果列表中。最后,返回结果列表即可。

三、具体代码

import java.util.ArrayList;
import java.util.List;

public class Solution {
    public List<List<Integer>> combine(int n, int k) {
        List<List<Integer>> result = new ArrayList<>();
        if (k <= 0 || n < k) {
            return result;
        }
        // 从1开始进行回溯
        backtrack(result, new ArrayList<>(), 1, n, k);
        return result;
    }

    private void backtrack(List<List<Integer>> result, List<Integer> comb, int start, int n, int k) {
        if (comb.size() == k) {
            // 找到一个有效的组合
            result.add(new ArrayList<>(comb));
            return;
        }

        for (int i = start; i <= n; i++) {
            // 添加当前数字到组合中
            comb.add(i);
            // 递归地继续向组合中添加数字
            backtrack(result, comb, i + 1, n, k);
            // 回溯:移除最后一个数字,尝试下一个数字
            comb.remove(comb.size() - 1);
        }
    }
}

四、时间复杂度和空间复杂度

1. 时间复杂度
  • 对于每个数字,我们都有两种选择:包含在组合中或不包含。
  • 对于n个数字,共有2^n种可能的组合,但题目要求我们只考虑大小为k的组合。
  • 因此,我们不会探索所有2^n种可能性,而是根据组合数公式C(n, k)进行操作。
  • 组合数公式C(n, k)表示从n个不同元素中取出k个元素的组合数,计算公式为C(n, k) = n! / (k! * (n-k)!).
  • 因此,时间复杂度是O(C(n, k)),即O(n! / (k! * (n-k)!))。
2. 空间复杂度
  • 空间复杂度主要取决于递归栈的深度和用于存储组合的列表。
  • 递归栈的深度在最坏情况下是k,即当我们要找到一个大小为k的组合时,递归调用会进行k次。
  • 每个组合需要一个大小为k的列表来存储,因此空间复杂度是O(k)。
  • 综上所述,总的空间复杂度是O(k)。

五、总结知识点

  1. 回溯算法:这是一种通过探索所有可能的候选解来找出所有的解的算法。如果候选解被确认不是一个解(或者至少不是最后一个解),回溯算法会通过在上一步进行一些变化丢弃该解,即回溯并且再次尝试。

  2. 递归backtrack 函数是一个递归函数,它通过自己调用自己来进行深度优先搜索(DFS)。递归允许函数在执行过程中调用自己,从而可以遍历所有可能的组合。

  3. 列表(ArrayList)result 和 comb 都是 ArrayList 类型的列表,用于存储组合结果和当前的组合。ArrayList 是 Java 中的一个可调整大小的数组实现,提供了对元素的快速随机访问。

  4. 条件语句:代码中使用了条件语句(if)来检查组合的大小是否达到了k,以及输入参数是否有效。

  5. 循环语句for 循环用于遍历所有可能的数字,从 start 到 n

  6. 列表操作add 和 remove 方法用于在组合列表中添加和移除元素。这是对列表进行动态操作的基本方法。

  7. 函数定义和调用:代码中定义了 combine 和 backtrack 两个函数,combine 是公共函数,用于对外提供接口,而 backtrack 是一个私有辅助函数,用于实现回溯逻辑。

  8. 参数传递:函数通过值传递(int n, int k)和引用传递(List<List<Integer>> result, List<Integer> comb)来传递参数。在 Java 中,基本数据类型(如 int)是通过值传递的,而对象(如 List)是通过引用传递的。

  9. 深拷贝和浅拷贝:在将 comb 添加到 result 时,使用了 new ArrayList<>(comb) 来创建 comb 的深拷贝。这是因为 ArrayList 是可变的,如果不进行深拷贝,result 中存储的将是 comb 引用的副本,而不是其值的副本。

以上就是解决这个问题的详细步骤,希望能够为各位提供启发和帮助。

相关推荐

  1. LeetCode练习总结组合-77

    2024-04-29 00:00:03       13 阅读
  2. LeetCode练习总结组合总和

    2024-04-29 00:00:03       20 阅读
  3. LeetCode练习总结:爬楼梯--70

    2024-04-29 00:00:03       11 阅读
  4. LeetCode练习总结:编辑距离--72

    2024-04-29 00:00:03       12 阅读
  5. LeetCode练习总结:解数独

    2024-04-29 00:00:03       21 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-29 00:00:03       19 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-29 00:00:03       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-29 00:00:03       20 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-29 00:00:03       20 阅读

热门阅读

  1. new qemu QEMU_OPTION_d

    2024-04-29 00:00:03       14 阅读
  2. 笨蛋学C++【C++基础第八弹】

    2024-04-29 00:00:03       15 阅读
  3. C语言基础—多线程基础

    2024-04-29 00:00:03       12 阅读
  4. YOLOV5 TensorRT部署 BatchedNMS(转换engine模型)(上)

    2024-04-29 00:00:03       11 阅读
  5. 在Docker中为Nginx容器添加多端口映射的详细指南

    2024-04-29 00:00:03       9 阅读
  6. 描述一下PHP中的MVC设计模式

    2024-04-29 00:00:03       10 阅读
  7. Linux系统使用命令来查看本地端口的使用情况

    2024-04-29 00:00:03       13 阅读
  8. Linux Makefile编写之可执行程序

    2024-04-29 00:00:03       47 阅读
  9. 先出发再思考怎么解决问题

    2024-04-29 00:00:03       38 阅读
  10. IDEA那些牛X的插件

    2024-04-29 00:00:03       16 阅读