【力扣算法】77-组合

题目

给定两个整数 n 和 k,返回 1 … n 中所有可能的 k 个数的组合。

示例:

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/combinations
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题解

方法一 : 回溯法

算法

回溯法 是一种通过遍历所有可能成员来寻找全部可行解的算法。若候选 不是 可行解 (或者至少不是 最后一个 解),回溯法会在前一步进行一些修改以舍弃该候选,换而言之, 回溯 并再次尝试。

这是一个回溯法函数,它将第一个添加到组合中的数和现有的组合作为参数。 backtrack(first, curr)

若组合完成- 添加到输出中。

遍历从 first t到 n的所有整数。

将整数 i 添加到现有组合 curr中。

继续向组合中添加更多整数 : backtrack(i + 1, curr).

将 i 从 curr中移除,实现回溯。

实现

class Solution {
  List<List<Integer>> output = new LinkedList();
  int n;
  int k;

  public void backtrack(int first, LinkedList<Integer> curr) {
    // if the combination is done
    if (curr.size() == k)
      output.add(new LinkedList(curr));

    for (int i = first; i < n + 1; ++i) {
      // add i into the current combination
      curr.add(i);
      // use next integers to complete the combination
      backtrack(i + 1, curr);
      // backtrack
      curr.removeLast();
    }
  }

  public List<List<Integer>> combine(int n, int k) {
    this.n = n;
    this.k = k;
    backtrack(1, new LinkedList<Integer>());
    return output;
  }
}

复杂度分析

时间复杂度 : O ( k C N k ) O(k C_N^k) O(kCNk),其中 C N k = N ! ( N − k ) ! k ! C_N^k = \frac{N!}{(N - k)! k!} CNk=(Nk)!k!N! 是要构成的组合数。 append / pop (add / removeLast) 操作使用常数时间,唯一耗费时间的是将长度为 k 的组合添加到输出中。

空间复杂度 : O ( C N k ) O(C_N^k) O(CNk),用于保存全部组合数以输出。

方法二: 字典序 (二进制排序) 组合

直觉

主要思路是以字典序的顺序获得全部组合。

算法

算法非常直截了当 :

将 nums 初始化为从 1 到 k的整数序列。 将 n + 1添加为末尾元素,起到“哨兵”的作用。 将指针设为列表的开头 j = 0.

While j < k :

将nums 中的前k个元素添加到输出中,换而言之,除了“哨兵”之外的全部元素。

找到nums中的第一个满足 nums[j] + 1 != nums[j + 1]的元素,并将其加一 nums[j]++ 以转到下一个组合。

实现

class Solution {
  public List<List<Integer>> combine(int n, int k) {
    // init first combination
    LinkedList<Integer> nums = new LinkedList<Integer>();
    for(int i = 1; i < k + 1; ++i)
      nums.add(i);
    nums.add(n + 1);

    List<List<Integer>> output = new ArrayList<List<Integer>>();
    int j = 0;
    while (j < k) {
      // add current combination
      output.add(new LinkedList(nums.subList(0, k)));
      // increase first nums[j] by one
      // if nums[j] + 1 != nums[j + 1]
      j = 0;
      while ((j < k) && (nums.get(j + 1) == nums.get(j) + 1))
        nums.set(j, j++ + 1);
      nums.set(j, nums.get(j) + 1);
    }
    return output;
  }
}

复杂度分析

时间复杂度 : O ( k C N k ) O(k C_N^k) O(kCNk),其中 C N k = N ! ( N − k ) ! k ! C_N^k = \frac{N!}{(N - k)! k!} CNk=(Nk)!k!N! 是要构建的组合数。由于组合数是 C N k C_N^k CNk,外层的 while 循环执行了 C N k C_N^k CNk 次 。对给定的一个j,内层的 while 循环执行了 C N − j k − j C_{N - j}^{k - j} CNjkj 次。外层循环超过 C N k C_N^k CNk 次访问,平均而言每次访问的执行次数少于1。因此,最耗费时间的部分是将每个长度为k 的组合(共计 C N k C_N^k CNk个组合) 添加到输出中, 消耗 O ( k C N k ) O(k C_N^k) O(kCNk) 的时间。

你可能注意到,尽管方法二的时间复杂度与方法一相同,但方法二却要快上许多。这是由于方法一需要处理递归调用栈,且其带来的影响在Python中比在Java中更为显著。

空间复杂度 : O ( C N k ) O(C_N^k) O(CNk) ,用于保存全部组合数以输出。

作者:LeetCode
链接:https://leetcode-cn.com/problems/two-sum/solution/zu-he-by-leetcode/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

感想

执行用时 :68 ms, 在所有 Java 提交中击败了39.81%的用户

内存消耗 :41.9 MB, 在所有 Java 提交中击败了85.64%的用户

class Solution {
    List<List<Integer>> res;
    int k = 0;
    int n = 0;
    public List<List<Integer>> combine(int n, int k) {
        res = new LinkedList<>();
        if(k<=0) return res;
        this.k = k;
        this.n = n;
        LinkedList<Integer> list = new LinkedList<>();
        backtrack(0,list);
        return res;
    }
    private void backtrack(int i,LinkedList<Integer> list){
        if(list.size()==k) res.add(new LinkedList<>(list));
        else{
            for(int j=i;j<n;j++){
                list.add(j+1);
                backtrack(j+1,list);
                list.removeLast();
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值